]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
mail_index_sync_sort_flags() now merges flag changes so mail storage
authorTimo Sirainen <tss@iki.fi>
Sat, 3 Jul 2004 17:21:26 +0000 (20:21 +0300)
committerTimo Sirainen <tss@iki.fi>
Sat, 3 Jul 2004 17:21:26 +0000 (20:21 +0300)
backends don't need to do it (and maildir didn't before). Dirty flags will
be synced now too.

--HG--
branch : HEAD

src/lib-index/mail-index-sync.c
src/lib-index/mail-index.h
src/lib-index/mail-transaction-util.c
src/lib-storage/index/maildir/maildir-sync.c

index 1d49113d995f5a9946d59722d46e0396f4628d1a..bc6fb6122756d3cb0f1b4d3cbcfcd91ab62956a0 100644 (file)
 
 #include <stdlib.h>
 
-static void mail_index_sync_sort_flags(struct mail_index_sync_ctx *ctx)
+static void
+mail_index_sync_sort_flags(buffer_t *dest_buf,
+                          const struct mail_transaction_flag_update *src,
+                          size_t src_size)
 {
-       const struct mail_transaction_flag_update *src, *src_end;
-       const struct mail_transaction_flag_update *dest;
-       struct mail_transaction_flag_update new_update;
-       uint32_t last;
+       const struct mail_transaction_flag_update *src_end;
+       struct mail_transaction_flag_update *dest;
+       struct mail_transaction_flag_update new_update, tmp_update;
        size_t i, dest_count;
+       int j;
 
-       src = ctx->data;
-       src_end = PTR_OFFSET(src, ctx->hdr->size);
-       if (src == src_end)
-               return;
-
-       dest = buffer_get_data(ctx->updates_buf, &dest_count);
+       dest = buffer_get_modifyable_data(dest_buf, &dest_count);
        dest_count /= sizeof(*dest);
 
+       if (dest_count == 0) {
+               buffer_append(dest_buf, src, src_size);
+               return;
+       }
+
+       src_end = PTR_OFFSET(src, src_size);
        for (i = 0; src != src_end; src++) {
                new_update = *src;
 
-               /* insert it into buffer, split it in multiple parts if needed
-                  to make sure the ordering stays the same */
+               /* insert it into buffer, split and merge it with existing
+                  updates if needed. */
                for (; i < dest_count; i++) {
-                       if (dest[i].uid1 <= new_update.uid1)
+                       if (new_update.uid1 > dest[i].uid2)
                                continue;
 
-                       if (dest[i].uid1 > new_update.uid2)
+                       if (new_update.uid2 < dest[i].uid1)
                                break;
 
-                       /* partial */
-                       last = new_update.uid2;
-                       new_update.uid2 = dest[i].uid1-1;
+                       /* at least partially overlapping */
+
+                       if (new_update.uid1 < dest[i].uid1) {
+                               /* { 5..6 } + { 1..5 } -> { 1..4 } + { 5..6 } */
+                               tmp_update = new_update;
+                               tmp_update.uid2 = dest[i].uid1-1;
+                               new_update.uid1 = dest[i].uid1;
+                               buffer_insert(dest_buf, i * sizeof(tmp_update),
+                                             &tmp_update, sizeof(tmp_update));
+                               dest = buffer_get_modifyable_data(dest_buf,
+                                                                 NULL);
+                               dest_count++; i++;
+                       } else if (new_update.uid1 > dest[i].uid1) {
+                               /* { 5..7 } + { 6..6 } ->
+                                  split old to { 5..5 } + { 6..7 } */
+                               tmp_update = dest[i];
+                               tmp_update.uid2 = new_update.uid1-1;
+                               dest[i].uid1 = new_update.uid1;
+                               buffer_insert(dest_buf, i * sizeof(tmp_update),
+                                             &tmp_update, sizeof(tmp_update));
+                               dest = buffer_get_modifyable_data(dest_buf,
+                                                                 NULL);
+                               dest_count++; i++;
+                       }
+                       i_assert(new_update.uid1 == dest[i].uid1);
+
+                       if (new_update.uid2 < dest[i].uid2) {
+                               /* { 5..7 } + { 5..6 } -> { 5..6 } + { 7..7 } */
+                               tmp_update = dest[i];
+                               tmp_update.uid1 = new_update.uid2+1;
+                               dest[i].uid2 = new_update.uid2;
+                               buffer_insert(dest_buf,
+                                             (i+1) * sizeof(tmp_update),
+                                             &tmp_update, sizeof(tmp_update));
+                               dest = buffer_get_modifyable_data(dest_buf,
+                                                                 NULL);
+                               dest_count++;
+                               new_update.uid2 = 0;
+                       } else {
+                               /* full match, or continues. */
+                               new_update.uid1 = dest[i].uid2+1;
+                       }
 
-                       buffer_insert(ctx->updates_buf, i * sizeof(new_update),
+                       /* dest[i] now contains the overlapping area.
+                          merge them - new_update overrides old changes. */
+                       dest[i].add_flags |= new_update.add_flags;
+                       dest[i].add_flags &= ~new_update.remove_flags;
+                       dest[i].remove_flags |= new_update.remove_flags;
+                       dest[i].remove_flags &= ~new_update.add_flags;
+
+                       for (j = 0; j < INDEX_KEYWORDS_BYTE_COUNT; j++) {
+                               dest[i].add_keywords[j] |=
+                                       new_update.add_keywords[j];
+                               dest[i].add_keywords[j] &=
+                                       ~new_update.remove_keywords[j];
+                               dest[i].remove_keywords[j] |=
+                                       new_update.remove_keywords[j];
+                               dest[i].remove_keywords[j] &=
+                                       ~new_update.add_keywords[j];
+                       }
+               }
+
+               if (new_update.uid1 <= new_update.uid2) {
+                       buffer_insert(dest_buf, i * sizeof(new_update),
                                      &new_update, sizeof(new_update));
-                       dest = buffer_get_data(ctx->updates_buf, NULL);
+                       dest = buffer_get_modifyable_data(dest_buf, NULL);
                        dest_count++;
-
-                       new_update.uid1 = new_update.uid2+1;
-                       new_update.uid2 = last;
                }
-
-               buffer_insert(ctx->updates_buf, i * sizeof(new_update),
-                             &new_update, sizeof(new_update));
-               dest = buffer_get_data(ctx->updates_buf, NULL);
-               dest_count++;
        }
 }
 
@@ -62,23 +117,12 @@ static void mail_index_sync_sort_transaction(struct mail_index_sync_ctx *ctx)
 {
        switch (ctx->hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
        case MAIL_TRANSACTION_EXPUNGE:
-               if (buffer_get_used_size(ctx->expunges_buf) == 0) {
-                       buffer_append(ctx->expunges_buf, ctx->data,
-                                     ctx->hdr->size);
-               } else {
-                       mail_transaction_log_sort_expunges(ctx->expunges_buf,
-                                                          ctx->data,
-                                                          ctx->hdr->size);
-               }
+               mail_transaction_log_sort_expunges(ctx->expunges_buf,
+                                                  ctx->data, ctx->hdr->size);
                break;
        case MAIL_TRANSACTION_FLAG_UPDATE:
-               if (buffer_get_used_size(ctx->expunges_buf) == 0 &&
-                   buffer_get_used_size(ctx->updates_buf) == 0) {
-                       buffer_append(ctx->updates_buf, ctx->data,
-                                     ctx->hdr->size);
-               } else {
-                       mail_index_sync_sort_flags(ctx);
-               }
+               mail_index_sync_sort_flags(ctx->updates_buf, ctx->data,
+                                          ctx->hdr->size);
                break;
        case MAIL_TRANSACTION_APPEND: {
                const struct mail_transaction_append_header *hdr = ctx->data;
@@ -99,11 +143,48 @@ static void mail_index_sync_sort_transaction(struct mail_index_sync_ctx *ctx)
        }
 }
 
+static int mail_index_sync_add_dirty_updates(struct mail_index_sync_ctx *ctx)
+{
+       struct mail_transaction_flag_update update;
+       const struct mail_index_record *rec;
+       uint32_t seq, messages_count;
+       int i;
+
+       memset(&update, 0, sizeof(update));
+
+       messages_count = mail_index_view_get_message_count(ctx->view);
+       for (seq = 1; seq <= messages_count; seq++) {
+               if (mail_index_lookup(ctx->view, seq, &rec) < 0)
+                       return -1;
+
+               if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) == 0)
+                       continue;
+
+               update.uid1 = update.uid2 = rec->uid;
+               update.add_flags = rec->flags;
+               update.remove_flags = ~update.add_flags;
+               memcpy(update.add_keywords, rec->keywords,
+                      INDEX_KEYWORDS_BYTE_COUNT);
+               for (i = 0; i < INDEX_KEYWORDS_BYTE_COUNT; i++)
+                       update.remove_keywords[i] = ~update.add_keywords[i];
+
+               mail_index_sync_sort_flags(ctx->updates_buf,
+                                          &update, sizeof(update));
+       }
+       return 0;
+}
+
 static int mail_index_sync_read_and_sort(struct mail_index_sync_ctx *ctx)
 {
        size_t size;
        int ret;
 
+       if (ctx->view->map->hdr->flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) {
+               /* show dirty flags as flag updates */
+               if (mail_index_sync_add_dirty_updates(ctx) < 0)
+                       return -1;
+       }
+
        while ((ret = mail_transaction_log_view_next(ctx->view->log_view,
                                                     &ctx->hdr,
                                                     &ctx->data, NULL)) > 0) {
@@ -260,42 +341,22 @@ int mail_index_sync_next(struct mail_index_sync_ctx *ctx,
        next_update = ctx->update_idx == ctx->updates_count ? NULL :
                &ctx->updates[ctx->update_idx];
 
-       // FIXME: return dirty flagged records as flag updates
-
-       /* the ugliness here is to avoid returning overlapping expunge
-          and update areas. For example:
-
-          updates[] = A { 1, 7 }, B { 1, 3 }
-          expunges[] = { 5, 6 }
-
-          will make us return
-
-          update A: 1, 4
-          update B: 1, 3
-          expunge : 5, 6
-          update A: 7, 7
-       */
-       while (next_update != NULL &&
-              (next_exp == NULL || next_update->uid1 < next_exp->uid1)) {
-               if (next_update->uid2 >= ctx->next_uid) {
-                       mail_index_sync_get_update(sync_rec, next_update);
-                       if (next_exp != NULL &&
-                           next_exp->uid1 <= next_update->uid2) {
-                               /* it's overlapping.. */
-                               sync_rec->uid2 = next_exp->uid1-1;
-                       }
-
-                       if (sync_rec->uid1 < ctx->next_uid)
-                               sync_rec->uid1 = ctx->next_uid;
+       if (next_update != NULL &&
+           (next_exp == NULL || next_update->uid1 < next_exp->uid1)) {
+               mail_index_sync_get_update(sync_rec, next_update);
+               if (next_exp != NULL && next_exp->uid1 <= next_update->uid2) {
+                       /* it's overlapping with next expunge */
+                       sync_rec->uid2 = next_exp->uid1-1;
+               }
 
-                       i_assert(sync_rec->uid1 <= sync_rec->uid2);
-                       ctx->update_idx++;
-                       return mail_index_sync_rec_check(ctx->view, sync_rec);
+               if (sync_rec->uid1 < ctx->next_uid) {
+                       /* overlapping with previous expunge */
+                       sync_rec->uid1 = ctx->next_uid;
                }
 
-               if (++ctx->update_idx == ctx->updates_count)
-                       break;
-               next_update++;
+               i_assert(sync_rec->uid1 <= sync_rec->uid2);
+               ctx->update_idx++;
+               return mail_index_sync_rec_check(ctx->view, sync_rec);
        }
 
        if (next_exp != NULL) {
@@ -304,9 +365,6 @@ int mail_index_sync_next(struct mail_index_sync_ctx *ctx,
                        return -1;
 
                ctx->expunge_idx++;
-
-               /* scan updates again from the beginning */
-               ctx->update_idx = 0;
                ctx->next_uid = next_exp->uid2+1;
                return 1;
        }
index e9afe9e39e5df7147b483d7cdbd3c7be9fed2d91..be346fc001b9b6de42bfb5bbe2786b1d95e88e66 100644 (file)
@@ -194,14 +194,13 @@ mail_index_transaction_open_updated_view(struct mail_index_transaction *t);
 
    mail_index_sync_next() returns all changes from previously committed
    transactions which haven't yet been committed to the actual mailbox.
-   They're returned in ascending order. You must go through all of them and
-   update the mailbox accordingly.
+   They're returned in ascending order and they never overlap (if we add more
+   sync types, then they might). You must go through all of them and update
+   the mailbox accordingly.
 
    None of the changes actually show up in index until at
    mail_index_sync_end().
 
-   Note that there may be multiple overlapping flag changes. They're returned
-   sorted by their beginning sequence. They never overlap expunges however.
    Returned sequence numbers describe the mailbox state at the beginning of
    synchronization, ie. expunges don't affect them.
 
index 5fa3e7683e4c17f4c2c55b0f873f26d5dbcae6cc..5160b57e0ef2d94eacbadea49fd4c1803f55f979 100644 (file)
@@ -189,12 +189,17 @@ mail_transaction_log_sort_expunges(buffer_t *expunges_buf,
        size_t first, i, dest_count;
 
        i_assert(src_buf_size % sizeof(*src) == 0);
-       src_end = CONST_PTR_OFFSET(src, src_buf_size);
 
        /* @UNSAFE */
        dest = buffer_get_modifyable_data(expunges_buf, &dest_count);
        dest_count /= sizeof(*dest);
 
+       if (dest_count == 0) {
+               buffer_append(expunges_buf, src, src_buf_size);
+               return;
+       }
+
+       src_end = CONST_PTR_OFFSET(src, src_buf_size);
        for (i = 0; src != src_end; src++) {
                /* src[] must be sorted. */
                i_assert(src+1 == src_end || src->uid1 < src[1].uid1);
index 5364ab316011d4c16eae452f91d6420abbfefa9d..304a7825458bfc29af66377518184c532b90536c 100644 (file)
@@ -204,7 +204,7 @@ struct maildir_index_sync_context {
 
        struct mail_index_sync_rec sync_rec;
        uint32_t seq;
-       int have_dirty, last_dirty;
+       int dirty_state;
 };
 
 static int maildir_expunge(struct index_mailbox *ibox, const char *path,
@@ -231,7 +231,7 @@ static int maildir_sync_flags(struct index_mailbox *ibox, const char *path,
        uint8_t flags8;
         keywords_mask_t keywords;
 
-       ctx->last_dirty = FALSE;
+       ctx->dirty_state = 0;
 
        (void)maildir_filename_get_flags(path, &flags, keywords);
 
@@ -240,6 +240,8 @@ static int maildir_sync_flags(struct index_mailbox *ibox, const char *path,
 
        newpath = maildir_filename_set_flags(path, flags8, keywords);
        if (rename(path, newpath) == 0) {
+               if ((flags8 & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
+                       ctx->dirty_state = -1;
                ibox->dirty_cur_time = ioloop_time;
                return 1;
        }
@@ -250,7 +252,7 @@ static int maildir_sync_flags(struct index_mailbox *ibox, const char *path,
                memset(keywords, 0, sizeof(keywords));
                mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_ADD,
                                        MAIL_INDEX_MAIL_FLAG_DIRTY, keywords);
-               ctx->last_dirty = TRUE;
+               ctx->dirty_state = 1;
                return 1;
        }
 
@@ -264,7 +266,6 @@ static int maildir_sync_record(struct index_mailbox *ibox,
 {
        struct mail_index_sync_rec *sync_rec = &ctx->sync_rec;
        struct mail_index_view *view = ctx->view;
-       const struct mail_index_record *rec;
        uint32_t seq, seq1, seq2, uid;
 
        switch (sync_rec->type) {
@@ -304,19 +305,14 @@ static int maildir_sync_record(struct index_mailbox *ibox,
                        if (maildir_file_do(ibox, uid,
                                            maildir_sync_flags, ctx) < 0)
                                return -1;
-                       if (!ctx->last_dirty) {
-                               /* if this flag was dirty, drop it */
-                               if (mail_index_lookup(view, ctx->seq, &rec) < 0)
-                                       return -1;
-                               if (rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) {
-                                       keywords_mask_t keywords;
-
-                                       memset(keywords, 0, sizeof(keywords));
-                                       mail_index_update_flags(ctx->trans,
-                                               ctx->seq, MODIFY_REMOVE,
-                                               MAIL_INDEX_MAIL_FLAG_DIRTY,
-                                               keywords);
-                               }
+                       if (ctx->dirty_state < 0) {
+                               /* flag isn't dirty anymore */
+                               keywords_mask_t keywords;
+
+                               memset(keywords, 0, sizeof(keywords));
+                               mail_index_update_flags(ctx->trans, ctx->seq,
+                                       MODIFY_REMOVE,
+                                       MAIL_INDEX_MAIL_FLAG_DIRTY, keywords);
                        }
                }
                break;
@@ -328,7 +324,6 @@ static int maildir_sync_record(struct index_mailbox *ibox,
 int maildir_sync_last_commit(struct index_mailbox *ibox)
 {
        struct maildir_index_sync_context ctx;
-       const struct mail_index_header *hdr;
        uint32_t seq;
        uoff_t offset;
        int ret;
@@ -344,10 +339,6 @@ int maildir_sync_last_commit(struct index_mailbox *ibox)
                                    ibox->commit_log_file_seq,
                                    ibox->commit_log_file_offset);
        if (ret > 0) {
-               if (mail_index_get_header(ctx.view, &hdr) == 0 &&
-                   (hdr->flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) != 0)
-                       ctx.have_dirty = TRUE;
-
                ctx.trans = mail_index_transaction_begin(ctx.view, FALSE);
 
                while ((ret = mail_index_sync_next(ctx.sync_ctx,