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 \
#include "mail-index.h"
#include "mail-index-data.h"
#include "mail-index-util.h"
-#include "mail-tree.h"
#include <stdio.h>
#include <unistd.h>
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)
{
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);
(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;
--- /dev/null
+/* 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;
+}
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);
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;
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);
#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"
}
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;
}
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. */
#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"
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;
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;
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);
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)
}
}
-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) +
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;
}
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;
}
index->mmap_used_length -= sizeof(*rec);
} else {
/* mark it deleted */
- update_first_hole(index, rec);
+ (void)mail_index_expunge_record_range(index, rec, rec);
}
}
#include "message-parser.h"
#include "imap-util.h"
-#define MAIL_INDEX_VERSION 2
+#define MAIL_INDEX_VERSION 3
#define INDEX_FILE_PREFIX ".imap.index"
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
uoff_t used_file_size;
- unsigned int first_hole_index;
- unsigned int first_hole_records;
-
unsigned int uid_validity;
unsigned int next_uid;
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 {
/* 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);
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
/* private: */
struct mail_index_data *data;
- struct mail_tree *tree;
struct mail_modify_log *modifylog;
struct mail_custom_flags *custom_flags;
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, \
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,
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,
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 */
#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) \
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;
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))
/* 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);
+++ /dev/null
-/*
- 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;
-}
+++ /dev/null
-/* 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++;
- }
-}
+++ /dev/null
-#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
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;
{
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) {
return FALSE;
}
- seq = 0;
+ seq = 1;
rec = index->lookup(index, 1);
uidlist = ctx->uidlist;
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) {
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",
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);
}
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 |
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;
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))
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;
}
}
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)) {
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;
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);
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();
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) {
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;
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))
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
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);
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);