static int
mail_index_transaction_cache_commit(struct mail_index_transaction *t,
- uint32_t *log_file_seq_r,
- uoff_t *log_file_offset_r)
+ struct mail_index_transaction_commit_result *result_r)
{
struct mail_cache_transaction_ctx *ctx = CACHE_TRANS_CONTEXT(t);
struct mail_index_transaction_vfuncs super = ctx->super;
mail_cache_transaction_commit(&ctx);
- return super.commit(t, log_file_seq_r, log_file_offset_r);
+ return super.commit(t, result_r);
}
static void
ext = array_idx(&view->map->extensions, ext_map_idx);
modseqp = PTR_OFFSET(rec, ext->record_offset);
- if (*modseqp < min_modseq)
+ if (*modseqp > min_modseq)
+ return 0;
+ else {
*modseqp = min_modseq;
- return 0;
+ return 1;
+ }
}
static uint64_t
uint32_t fsck_log_head_file_seq;
uoff_t fsck_log_head_file_offset;
+ /* syncing will update this if non-NULL */
+ struct mail_index_transaction_commit_result *sync_commit_result;
+
int lock_type, shared_lock_count, excl_lock_count;
unsigned int lock_id_counter;
enum file_lock_method lock_method;
return ret;
}
+static bool sync_update_ignored_change(struct mail_index_sync_map_ctx *ctx)
+{
+ struct mail_index_transaction_commit_result *result =
+ ctx->view->index->sync_commit_result;
+ uint32_t log_seq;
+ uoff_t log_offset, start_offset;
+
+ if (result == NULL)
+ return FALSE;
+
+ mail_transaction_log_view_get_prev_pos(ctx->view->log_view,
+ &log_seq, &log_offset);
+ if (log_seq != result->log_file_seq)
+ return FALSE;
+
+ start_offset = result->log_file_offset - result->commit_size;
+ if (log_offset < start_offset || log_offset >= result->log_file_offset)
+ return FALSE;
+
+ return TRUE;
+}
+
static void sync_uid_update(struct mail_index_sync_map_ctx *ctx,
uint32_t old_uid, uint32_t new_uid)
{
+ struct mail_index_view *view = ctx->view;
struct mail_index_map *map;
struct mail_index_record *rec;
uint32_t old_seq;
if (new_uid < ctx->view->map->hdr.next_uid) {
/* uid update is no longer possible */
+ if (sync_update_ignored_change(ctx))
+ view->index->sync_commit_result->ignored_uid_changes++;
return;
}
- if (!mail_index_lookup_seq(ctx->view, old_uid, &old_seq))
+ if (!mail_index_lookup_seq(view, old_uid, &old_seq))
return;
map = mail_index_sync_get_atomic_map(ctx);
const struct mail_transaction_modseq_update *end;
uint32_t seq;
uint64_t min_modseq, highest_modseq = 0;
+ int ret;
end = CONST_PTR_OFFSET(u, size);
for (; u < end; u++) {
u->modseq_low32;
if (highest_modseq < min_modseq)
highest_modseq = min_modseq;
- if (seq != 0 &&
- mail_index_modseq_set(view, seq, min_modseq) < 0) {
+
+ ret = seq == 0 ? 1 :
+ mail_index_modseq_set(view, seq, min_modseq);
+ if (ret < 0) {
mail_index_sync_set_corrupted(ctx,
"modseqs updated before they were enabled");
return -1;
}
+ if (ret == 0 && sync_update_ignored_change(ctx))
+ view->index->sync_commit_result->ignored_modseq_changes++;
}
mail_index_modseq_update_highest(ctx->modseq_ctx, highest_modseq);
struct mail_index *index;
struct mail_index_view *view;
struct mail_index_transaction *sync_trans, *ext_trans;
+ struct mail_index_transaction_commit_result *sync_commit_result;
enum mail_index_sync_flags flags;
const struct mail_transaction_header *hdr;
return FALSE;
}
+void mail_index_sync_set_commit_result(struct mail_index_sync_ctx *ctx,
+ struct mail_index_transaction_commit_result *result)
+{
+ ctx->sync_commit_result = result;
+}
+
void mail_index_sync_reset(struct mail_index_sync_ctx *ctx)
{
struct mail_index_sync_list *sync_list;
/* refresh the mapping with newly committed external transactions
and the synced expunges. sync using file handler here so that the
expunge handlers get called. */
+ index->sync_commit_result = ctx->sync_commit_result;
if (mail_index_map(ctx->index, MAIL_INDEX_SYNC_HANDLER_FILE) <= 0)
ret = -1;
+ index->sync_commit_result = NULL;
want_rotate = mail_transaction_log_want_rotate(index->log);
if (ret == 0 &&
struct mail_index_transaction_vfuncs {
void (*reset)(struct mail_index_transaction *t);
int (*commit)(struct mail_index_transaction *t,
- uint32_t *log_file_seq_r, uoff_t *log_file_offset_r);
+ struct mail_index_transaction_commit_result *result_r);
void (*rollback)(struct mail_index_transaction *t);
};
return 1;
}
-static int mail_index_transaction_commit_real(struct mail_index_transaction *t)
+static int
+mail_index_transaction_commit_real(struct mail_index_transaction *t,
+ uoff_t *commit_size_r)
{
struct mail_transaction_log *log = t->view->index->log;
bool external = (t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0;
mail_transaction_log_get_head(log, &log_seq2, &log_offset2);
i_assert(log_seq1 == log_seq2);
+ *commit_size_r = log_offset2 - log_offset1;
+
if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_HIDE) != 0 &&
log_offset1 != log_offset2) {
/* mark the area covered by this transaction hidden */
}
static int mail_index_transaction_commit_v(struct mail_index_transaction *t,
- uint32_t *log_file_seq_r,
- uoff_t *log_file_offset_r)
+ struct mail_index_transaction_commit_result *result_r)
{
struct mail_index *index = t->view->index;
bool changed;
mail_index_view_get_messages_count(t->view));
changed = MAIL_INDEX_TRANSACTION_HAS_CHANGES(t) || t->reset;
- if (!changed) {
- /* nothing to append */
- ret = 0;
- } else {
- ret = mail_index_transaction_commit_real(t);
- }
- mail_transaction_log_get_head(index->log, log_file_seq_r,
- log_file_offset_r);
+ ret = !changed ? 0 :
+ mail_index_transaction_commit_real(t, &result_r->commit_size);
+ mail_transaction_log_get_head(index->log, &result_r->log_file_seq,
+ &result_r->log_file_offset);
if (ret == 0 && !index->syncing && changed) {
/* if we're committing a normal transaction, we want to
be done later as MAIL_INDEX_SYNC_HANDLER_FILE so that
expunge handlers get run for the newly expunged messages
(and sync handlers that require HANDLER_FILE as well). */
+ index->sync_commit_result = result_r;
(void)mail_index_refresh(index);
+ index->sync_commit_result = NULL;
}
mail_index_transaction_unref(&t);
int mail_index_transaction_commit(struct mail_index_transaction **t)
{
- uint32_t log_seq;
- uoff_t log_offset;
+ struct mail_index_transaction_commit_result result;
- return mail_index_transaction_commit_get_pos(t, &log_seq, &log_offset);
+ return mail_index_transaction_commit_full(t, &result);
}
-int mail_index_transaction_commit_get_pos(struct mail_index_transaction **_t,
- uint32_t *log_file_seq_r,
- uoff_t *log_file_offset_r)
+int mail_index_transaction_commit_full(struct mail_index_transaction **_t,
+ struct mail_index_transaction_commit_result *result_r)
{
struct mail_index_transaction *t = *_t;
}
*_t = NULL;
- return t->v.commit(t, log_file_seq_r, log_file_offset_r);
+ memset(result_r, 0, sizeof(*result_r));
+ return t->v.commit(t, result_r);
}
void mail_index_transaction_rollback(struct mail_index_transaction **_t)
unsigned int hidden:1;
};
+struct mail_index_transaction_commit_result {
+ /* seq/offset points to end of transaction */
+ uint32_t log_file_seq;
+ uoff_t log_file_offset;
+ /* number of bytes in the written transaction.
+ all of it was written to the same file. */
+ uoff_t commit_size;
+
+ unsigned int ignored_uid_changes;
+ unsigned int ignored_modseq_changes;
+};
+
struct mail_index;
struct mail_index_map;
struct mail_index_view;
mail_index_transaction_begin(struct mail_index_view *view,
enum mail_index_transaction_flags flags);
int mail_index_transaction_commit(struct mail_index_transaction **t);
-int mail_index_transaction_commit_get_pos(struct mail_index_transaction **t,
- uint32_t *log_file_seq_r,
- uoff_t *log_file_offset_r);
+int mail_index_transaction_commit_full(struct mail_index_transaction **t,
+ struct mail_index_transaction_commit_result *result_r);
void mail_index_transaction_rollback(struct mail_index_transaction **t);
/* Discard all changes in the transaction. */
void mail_index_transaction_reset(struct mail_index_transaction *t);
/* Reset syncing to initial state after mail_index_sync_begin(), so you can
go through all the sync records again with mail_index_sync_next(). */
void mail_index_sync_reset(struct mail_index_sync_ctx *ctx);
+/* Update result when refreshing index at the end of sync. */
+void mail_index_sync_set_commit_result(struct mail_index_sync_ctx *ctx,
+ struct mail_index_transaction_commit_result *result);
/* Commit synchronization by writing all changes to mail index file. */
int mail_index_sync_commit(struct mail_index_sync_ctx **ctx);
/* Rollback synchronization - none of the changes listed by sync_next() are
return 0;
}
-void cydir_transaction_save_commit_post(struct mail_save_context *_ctx)
+void cydir_transaction_save_commit_post(struct mail_save_context *_ctx,
+ struct mail_index_transaction_commit_result *result)
{
struct cydir_save_context *ctx = (struct cydir_save_context *)_ctx;
_ctx->transaction = NULL; /* transaction is already freed */
+ mail_index_sync_set_commit_result(ctx->sync_ctx->index_sync_ctx,
+ result);
+
(void)cydir_sync_finish(&ctx->sync_ctx, TRUE);
cydir_transaction_save_rollback(_ctx);
}
void cydir_save_cancel(struct mail_save_context *ctx);
int cydir_transaction_save_commit_pre(struct mail_save_context *ctx);
-void cydir_transaction_save_commit_post(struct mail_save_context *ctx);
+void cydir_transaction_save_commit_post(struct mail_save_context *ctx,
+ struct mail_index_transaction_commit_result *result);
void cydir_transaction_save_rollback(struct mail_save_context *ctx);
#endif
return 0;
}
-void mdbox_transaction_save_commit_post(struct mail_save_context *_ctx)
+void mdbox_transaction_save_commit_post(struct mail_save_context *_ctx,
+ struct mail_index_transaction_commit_result *result)
{
struct mdbox_save_context *ctx = (struct mdbox_save_context *)_ctx;
_ctx->transaction = NULL; /* transaction is already freed */
+ mail_index_sync_set_commit_result(ctx->sync_ctx->index_sync_ctx,
+ result);
+
/* finish writing the mailbox APPENDs */
if (mdbox_sync_finish(&ctx->sync_ctx, TRUE) == 0) {
if (ctx->map_trans != NULL)
uint32_t seq, uoff_t *offset_r);
int mdbox_transaction_save_commit_pre(struct mail_save_context *ctx);
-void mdbox_transaction_save_commit_post(struct mail_save_context *ctx);
+void mdbox_transaction_save_commit_post(struct mail_save_context *ctx,
+ struct mail_index_transaction_commit_result *result);
void mdbox_transaction_save_rollback(struct mail_save_context *ctx);
int mdbox_copy(struct mail_save_context *ctx, struct mail *mail);
return 0;
}
-void sdbox_transaction_save_commit_post(struct mail_save_context *_ctx)
+void sdbox_transaction_save_commit_post(struct mail_save_context *_ctx,
+ struct mail_index_transaction_commit_result *result)
{
struct sdbox_save_context *ctx = (struct sdbox_save_context *)_ctx;
return;
}
+ mail_index_sync_set_commit_result(ctx->sync_ctx->index_sync_ctx,
+ result);
+
if (sdbox_sync_finish(&ctx->sync_ctx, TRUE) < 0)
ctx->ctx.failed = TRUE;
sdbox_save_file_get_file(struct mailbox_transaction_context *t, uint32_t seq);
int sdbox_transaction_save_commit_pre(struct mail_save_context *ctx);
-void sdbox_transaction_save_commit_post(struct mail_save_context *ctx);
+void sdbox_transaction_save_commit_post(struct mail_save_context *ctx,
+ struct mail_index_transaction_commit_result *result);
void sdbox_transaction_save_rollback(struct mail_save_context *ctx);
int sdbox_copy(struct mail_save_context *ctx, struct mail *mail);
enum mail_index_open_flags index_flags;
int (*save_commit_pre)(struct mail_save_context *save_ctx);
- void (*save_commit_post)(struct mail_save_context *save_ctx);
+ void (*save_commit_post)(struct mail_save_context *save_ctx,
+ struct mail_index_transaction_commit_result *result_r);
void (*save_rollback)(struct mail_save_context *save_ctx);
struct mail_index *index;
static int
index_transaction_index_commit(struct mail_index_transaction *index_trans,
- uint32_t *log_file_seq_r,
- uoff_t *log_file_offset_r)
+ struct mail_index_transaction_commit_result *result_r)
{
struct index_transaction_context *it =
MAIL_STORAGE_CONTEXT(index_trans);
if (ret < 0)
it->super.rollback(it->trans);
else {
- if (it->super.commit(it->trans, log_file_seq_r,
- log_file_offset_r) < 0) {
+ if (it->super.commit(it->trans, result_r) < 0) {
mail_storage_set_index_error(ibox);
ret = -1;
}
}
if (t->save_ctx != NULL)
- ibox->save_commit_post(t->save_ctx);
+ ibox->save_commit_post(t->save_ctx, result_r);
index_transaction_free(it);
return ret;
(struct index_transaction_context *)_t;
struct mail_index_transaction *itrans = t->trans;
struct index_mailbox *ibox = (struct index_mailbox *)_t->box;
+ struct mail_index_transaction_commit_result result;
int ret;
memset(changes_r, 0, sizeof(*changes_r));
changes_r->pool = pool_alloconly_create("transaction changes", 1024);
p_array_init(&changes_r->saved_uids, changes_r->pool, 32);
- p_array_init(&changes_r->updated_uids, changes_r->pool, 32);
_t->changes = changes_r;
- ret = mail_index_transaction_commit(&itrans);
+ ret = mail_index_transaction_commit_full(&itrans, &result);
+
+ changes_r->ignored_uid_changes = result.ignored_uid_changes;
+ changes_r->ignored_modseq_changes = result.ignored_modseq_changes;
+
i_assert(ibox->box.transaction_count > 0 ||
ibox->view->transactions == 0);
return ret;
return 0;
}
-void maildir_transaction_save_commit_post(struct mail_save_context *_ctx)
+void maildir_transaction_save_commit_post(struct mail_save_context *_ctx,
+ struct mail_index_transaction_commit_result *result ATTR_UNUSED)
{
struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
uint32_t seq);
int maildir_transaction_save_commit_pre(struct mail_save_context *ctx);
-void maildir_transaction_save_commit_post(struct mail_save_context *ctx);
+void maildir_transaction_save_commit_post(struct mail_save_context *ctx,
+ struct mail_index_transaction_commit_result *result);
void maildir_transaction_save_rollback(struct mail_save_context *ctx);
int maildir_copy(struct mail_save_context *ctx, struct mail *mail);
return ret;
}
-void mbox_transaction_save_commit_post(struct mail_save_context *_ctx)
+void mbox_transaction_save_commit_post(struct mail_save_context *_ctx,
+ struct mail_index_transaction_commit_result *result ATTR_UNUSED)
{
struct mbox_save_context *ctx = (struct mbox_save_context *)_ctx;
void mbox_save_cancel(struct mail_save_context *ctx);
int mbox_transaction_save_commit_pre(struct mail_save_context *ctx);
-void mbox_transaction_save_commit_post(struct mail_save_context *ctx);
+void mbox_transaction_save_commit_post(struct mail_save_context *ctx,
+ struct mail_index_transaction_commit_result *result);
void mbox_transaction_save_rollback(struct mail_save_context *ctx);
#endif
{
struct index_list_mailbox *ibox = INDEX_LIST_STORAGE_CONTEXT(box);
struct mail_index_transaction *trans;
+ struct mail_index_transaction_commit_result result;
const void *data;
const uint32_t *counter_p;
uint32_t *ext_id_p;
return -1;
}
- return mail_index_transaction_commit_get_pos(&trans, &ibox->log_seq,
- &ibox->log_offset);
+ if (mail_index_transaction_commit_full(&trans, &result) < 0)
+ return -1;
+
+ ibox->log_seq = result.log_file_seq;
+ ibox->log_offset = result.log_file_offset;
+ return 0;
}
static struct mailbox_sync_context *
uint32_t uid_validity;
/* UIDs assigned to saved messages. Not necessarily ascending. */
ARRAY_TYPE(seq_range) saved_uids;
- /* UIDs assigned to updated UIDs. Not necessarily the same as the
- requested new UIDs. */
- ARRAY_TYPE(seq_range) updated_uids;
+
+ /* number of uid/modseq changes that couldn't be changed as requested */
+ unsigned int ignored_uid_changes;
+ unsigned int ignored_modseq_changes;
};
struct mailbox_sync_rec {