virtual-sync.c revision 1ce59b0bd89ab1b90d38fa9b9d51a92bf99bb73a
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen/* Copyright (c) 2008-2016 Dovecot authors, see the included COPYING file */
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen const char *const *kw_all;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen /* messages expunged within this sync */
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen /* all messages in this sync, sorted by mailbox_id
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen (but unsorted inside it for now, since it doesn't matter) */
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen uint32_t all_mails_idx, all_mails_prev_mailbox_id;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void virtual_sync_set_uidvalidity(struct virtual_sync_context *ctx)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen offsetof(struct mail_index_header, uid_validity),
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void virtual_sync_external_flags(struct virtual_sync_context *ctx,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const char *const *kw_names;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (!mail_set_uid(bbox->sync_mail, real_uid)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* we may have reopened the mailbox, which could have
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen caused the mail to be expunged already. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* copy flags */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_update_flags(ctx->trans, vseq, MODIFY_REPLACE, flags);
b34fdb68d376d85b4880da4a4bdf67ae726a381bTimo Sirainen /* copy keywords */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen kw_names = mail_get_keywords(bbox->sync_mail);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen keywords = mail_index_keywords_create(ctx->index, kw_names);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_update_keywords(ctx->trans, vseq, MODIFY_REPLACE, keywords);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int virtual_sync_mail_uid_cmp(const void *p1, const void *p2)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const struct virtual_sync_mail *m1 = p1, *m2 = p2;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen if (m1->vrec.mailbox_id < m2->vrec.mailbox_id)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (m1->vrec.mailbox_id > m2->vrec.mailbox_id)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenvirtual_backend_box_sync_mail_set(struct virtual_backend_box *bbox)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen trans = mailbox_transaction_begin(bbox->box, 0);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int bbox_mailbox_id_cmp(struct virtual_backend_box *const *b1,
b34fdb68d376d85b4880da4a4bdf67ae726a381bTimo Sirainenvirtual_sync_get_backend_box(struct virtual_mailbox *mbox, const char *name,
b34fdb68d376d85b4880da4a4bdf67ae726a381bTimo Sirainen *bbox_r = virtual_backend_box_lookup_name(mbox, name);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen if (*bbox_r != NULL || !mbox->sync_initialized)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* another process just added a new mailbox.
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen we can't handle this currently. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_storage_set_error(mbox->box.storage, MAIL_ERROR_TEMP, t_strdup_printf(
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen "Backend mailbox '%s' added by another session. "
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainenint virtual_mailbox_ext_header_read(struct virtual_mailbox *mbox,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const char *box_path = mailbox_get_path(&mbox->box);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen const struct virtual_mail_index_header *ext_hdr;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const struct virtual_mail_index_mailbox_record *mailboxes;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen unsigned int i, count, ext_name_offset, ext_mailbox_count;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen mail_index_get_header_ext(view, mbox->virtual_ext_id,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mbox->prev_uid_validity == hdr->uid_validity &&
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mbox->prev_change_counter == ext_hdr->change_counter) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* fully refreshed */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mbox->search_args_crc32 != ext_hdr->search_args_crc32) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mbox->prev_change_counter = ext_hdr->change_counter;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_hdr->mailbox_count > INT_MAX/sizeof(*mailboxes)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_error("virtual index %s: Broken mailbox_count header",
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* update mailbox backends */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (i = 0; i < ext_mailbox_count; i++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (mailboxes[i].id > ext_hdr->highest_mailbox_id ||
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_error("virtual index %s: Broken mailbox id",
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_error("virtual index %s: Broken mailbox name_len",
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ext_name_offset + mailboxes[i].name_len > ext_size) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_error("virtual index %s: Broken mailbox list",
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const unsigned char *nameptr;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen nameptr = CONST_PTR_OFFSET(ext_data, ext_name_offset);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen name = t_strndup(nameptr, mailboxes[i].name_len);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (virtual_sync_get_backend_box(mbox, name, &bbox) < 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* mailbox no longer exists. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bbox->sync_uid_validity = mailboxes[i].uid_validity;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mbox->highest_mailbox_id = ext_hdr == NULL ? 0 :
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* assign new mailbox IDs if any are missing */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bboxes = array_get_modifiable(&mbox->backend_boxes, &count);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (i = 0; i < count; i++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bboxes[i]->mailbox_id = ++mbox->highest_mailbox_id;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* sort the backend mailboxes by mailbox_id. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen array_sort(&mbox->backend_boxes, bbox_mailbox_id_cmp);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void virtual_sync_ext_header_rewrite(struct virtual_sync_context *ctx)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_mail_index_mailbox_record mailbox;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bboxes = array_get_modifiable(&ctx->mbox->backend_boxes, &count);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen name_pos = mailbox_pos + sizeof(mailbox) * count;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_hdr.change_counter = ++ctx->mbox->prev_change_counter;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_hdr.highest_mailbox_id = ctx->mbox->highest_mailbox_id;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_hdr.search_args_crc32 = ctx->mbox->search_args_crc32;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen buf = buffer_create_dynamic(pool_datastack_create(), name_pos + 256);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen buffer_append(buf, &ext_hdr, sizeof(ext_hdr));
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (i = 0; i < count; i++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bboxes[i]->mailbox_id > bboxes[i-1]->mailbox_id);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mailbox.uid_validity = bboxes[i]->sync_uid_validity;
812883e7758a86f615b9508ef10df1339123da83Timo Sirainen mailbox.highest_modseq = bboxes[i]->ondisk_highest_modseq;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen buffer_write(buf, mailbox_pos, &mailbox, sizeof(mailbox));
812883e7758a86f615b9508ef10df1339123da83Timo Sirainen buffer_write(buf, name_pos, bboxes[i]->name, mailbox.name_len);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_get_header_ext(ctx->sync_view, ctx->mbox->virtual_ext_id,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_ext_resize(ctx->trans, ctx->mbox->virtual_ext_id,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen mail_index_update_header_ext(ctx->trans, ctx->mbox->virtual_ext_id,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void virtual_sync_ext_header_update(struct virtual_sync_context *ctx)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* we changed something - update the change counter in header */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_hdr.change_counter = ++ctx->mbox->prev_change_counter;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_update_header_ext(ctx->trans, ctx->mbox->virtual_ext_id,
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen offsetof(struct virtual_mail_index_header, change_counter),
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen &ext_hdr.change_counter, sizeof(ext_hdr.change_counter));
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int virtual_sync_index_rec(struct virtual_sync_context *ctx,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (!mail_index_lookup_seq_range(ctx->sync_view,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* already expunged, nothing to do. */
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen mail_index_lookup_ext(ctx->sync_view, vseq, virtual_ext_id,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bbox = virtual_backend_box_lookup(ctx->mbox, vrec->mailbox_id);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (virtual_backend_box_open(ctx->mbox, bbox) < 0) {
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen virtual_backend_box_accessed(ctx->mbox, bbox);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen if (!mail_set_uid(bbox->sync_mail, vrec->real_uid)) {
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen /* message is already expunged from backend mailbox. */
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen flags = sync_rec->add_flags & MAIL_FLAGS_NONRECENT;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen flags = sync_rec->remove_flags & MAIL_FLAGS_NONRECENT;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen kw_names[0] = ctx->kw_all[sync_rec->keyword_idx];
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen keywords = mailbox_keywords_create_valid(bbox->box,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int virtual_sync_index_changes(struct virtual_sync_context *ctx)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen keywords = mail_index_get_keywords(ctx->index);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ctx->kw_all = array_count(keywords) == 0 ? NULL :
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (virtual_sync_index_rec(ctx, &sync_rec) < 0)
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainenstatic void virtual_sync_index_finish(struct virtual_sync_context *ctx)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* mark the newly seen messages as recent */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (mail_index_lookup_seq_range(ctx->sync_view, hdr->first_recent_uid,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen mailbox_recent_flags_set_seqs(&ctx->mbox->box, ctx->sync_view,
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen /* entire mailbox list needs to be rewritten */
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen /* update only changed parts in the header */
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainenstatic int virtual_sync_backend_box_init(struct virtual_backend_box *bbox)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen enum mailbox_search_result_flags result_flags;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen trans = mailbox_transaction_begin(bbox->box, 0);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_search_args_init(bbox->search_args, bbox->box, FALSE, NULL);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen search_ctx = mailbox_search_init(trans, bbox->search_args, NULL,
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen /* save the result and keep it updated */
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen result_flags = MAILBOX_SEARCH_RESULT_FLAG_UPDATE |
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mailbox_search_result_save(search_ctx, result_flags);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* add the found UIDs to uidmap. virtual_uid gets assigned later. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen while (mailbox_search_next(search_ctx, &mail)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenvirtual_backend_uidmap_bsearch_cmp(const uint32_t *uidp,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenvirtual_sync_mailbox_box_remove(struct virtual_sync_context *ctx,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen unsigned int i, src, dest, uid_count, rec_count;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* everything in removed_uids should exist in bbox->uids */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uidmap = array_get_modifiable(&bbox->uids, &rec_count);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* find the first uidmap record to be removed */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (!array_bsearch_insert_pos(&bbox->uids, &uids[0].seq1,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* remove the unwanted messages */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (i = 0; i < uid_count; i++) {
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainenvirtual_sync_mailbox_box_add(struct virtual_sync_context *ctx,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen unsigned int i, src, dest, uid_count, add_count, rec_count;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen added_uids = array_get(added_uids_arr, &uid_count);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* none of added_uids should exist in bbox->uids. find the position
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen of the first inserted index. */
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen uidmap = array_get_modifiable(&bbox->uids, &rec_count);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen added_uids[0].seq1 > uidmap[rec_count-1].real_uid) {
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen /* fast path: usually messages are appended */
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen } else if (array_bsearch_insert_pos(&bbox->uids, &added_uids[0].seq1,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen /* make space for all added UIDs. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen array_idx_clear(&bbox->uids, dest + add_count-1);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen uidmap = array_get_modifiable(&bbox->uids, &rec_count);
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen /* add/move the UIDs to their correct positions */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (i = 0; i < uid_count; i++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen while (src < rec_count && uidmap[src].real_uid < add_uid)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (; add_uid <= added_uids[i].seq2; add_uid++, dest++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int virtual_backend_uidmap_cmp(const struct virtual_backend_uidmap *u1,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void virtual_sync_bbox_uids_sort(struct virtual_backend_box *bbox)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* the uidmap must be sorted by real_uids */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen array_sort(&bbox->uids, virtual_backend_uidmap_cmp);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void virtual_sync_backend_boxes_sort_uids(struct virtual_mailbox *mbox)
unsigned int i, count;
for (i = 0; i < count; i++) {
const void *data;
old_msg_count = 0;
&removed_uids);
unsigned int i, n = 0, count;
if (i == count)
for (; i < count; ) {
unsigned int i, n = 0, count;
for (; i < count; ) {
if (!iter_done) {
&added_uids);
&temp_uids);
&removed_uids);
&removed_uids);
unsigned int *idx1_r,
unsigned int *idx2_r)
&idx);
return FALSE;
return TRUE;
&idx1);
case MAILBOX_SYNC_TYPE_FLAGS:
case MAILBOX_SYNC_TYPE_MODSEQ:
const unsigned int uidval_pos =
unsigned int mailbox_offset;
int ret;
&status) < 0) {
} T_END;
return ret;
const void *data;
unsigned int j = 0, uidmap_count = 0;
if (messages == 0)
for (i = 0; i < messages; i++) {
for (; j < uidmap_count; j++) {
&uidmap_count);
for (; j < uidmap_count; j++) {
for (; j < uidmap_count; j++) {
for (i = 0; i < count; i++) {
for (j = 0; j < uidmap_count; j++) {
unsigned int i, count;
if (count == 0) {
&idx))
i_unreached();
const void *data;
const void *data;
const void *mail_data;
unsigned int i, count;
int ret;
for (i = 0; i < count; i++) {
#ifdef DEBUG
for (i = 0; i < count; i++) {
return ret;
unsigned int i, count;
for (i = 0; i < count; i++)
if (success) {
return ret;
bool broken;
int ret;
if (ret <= 0) {
if (ret < 0)
return ret;
if (ret < 0)
if (ret == 0)
if (broken)
struct mailbox_sync_context *
int ret = 0;
return sync_ctx;