mail-index-transaction.c revision d810ebb2cc4588d11508a59bae1d1a13cd374b07
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch/* Inside transaction we keep messages stored in sequences in uid fields.
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch Before they're written to transaction log the sequences are changed to
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch UIDs. This is because we're able to compress sequence ranges better. */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch#include "lib.h"
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch#include "buffer.h"
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch#include "mail-index-view-private.h"
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch#include "mail-transaction-log.h"
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch#include "mail-cache-private.h"
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch#include "mail-index-transaction-private.h"
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch#include <stddef.h>
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Boschstatic void mail_index_transaction_add_last(struct mail_index_transaction *t);
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Boschstruct mail_index_transaction *
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Boschmail_index_transaction_begin(struct mail_index_view *view, int hide)
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch{
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch struct mail_index_transaction *t;
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch /* don't allow syncing view while there's ongoing transactions */
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch mail_index_view_transaction_ref(view);
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch t = i_new(struct mail_index_transaction, 1);
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch t->refcount = 1;
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch t->view = view;
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch t->hide_transaction = hide;
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch t->first_new_seq = mail_index_view_get_message_count(t->view)+1;
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch return t;
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch}
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Boschstatic void mail_index_transaction_free(struct mail_index_transaction *t)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch buffer_t **recs;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size_t i, size;
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch mail_index_view_transaction_unref(t->view);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (t->extra_rec_updates != NULL) {
45324f1eafa565dbc65e4dd335de9507dead55e6Timo Sirainen recs = buffer_get_modifyable_data(t->extra_rec_updates, &size);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size /= sizeof(*recs);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch for (i = 0; i < size; i++) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (recs[i] != NULL)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch buffer_free(recs[i]);
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch buffer_free(t->extra_rec_updates);
833bed942977673526c72e79bccc09314fc57104Phil Carmody }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (t->appends != NULL)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch buffer_free(t->appends);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (t->expunges != NULL)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch buffer_free(t->expunges);
e9228a3918aa0243eff4aae1ff5462bd3198417fTimo Sirainen if (t->updates != NULL)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch buffer_free(t->updates);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (t->cache_updates != NULL)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch buffer_free(t->cache_updates);
1e9296de32c9ddda40f33c06556cd568ddadf71fTimo Sirainen if (t->extra_intros != NULL)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch buffer_free(t->extra_intros);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_free(t);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschvoid mail_index_transaction_ref(struct mail_index_transaction *t)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
1e9296de32c9ddda40f33c06556cd568ddadf71fTimo Sirainen t->refcount++;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Boschvoid mail_index_transaction_unref(struct mail_index_transaction *t)
833bed942977673526c72e79bccc09314fc57104Phil Carmody{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (--t->refcount == 0)
833bed942977673526c72e79bccc09314fc57104Phil Carmody mail_index_transaction_free(t);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschstatic void
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschmail_index_buffer_convert_to_uids(struct mail_index_transaction *t,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch buffer_t *buf, size_t record_size, int range)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch struct mail_index_view *view = t->view;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch const struct mail_index_record *rec;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch unsigned char *data;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size_t size, i;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch uint32_t *seq;
1e9296de32c9ddda40f33c06556cd568ddadf71fTimo Sirainen int j;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (buf == NULL)
faa8995f1d300e7a8917407a52bbd1b98e10bf25Timo Sirainen return;
faa8995f1d300e7a8917407a52bbd1b98e10bf25Timo Sirainen
faa8995f1d300e7a8917407a52bbd1b98e10bf25Timo Sirainen /* @UNSAFE */
faa8995f1d300e7a8917407a52bbd1b98e10bf25Timo Sirainen data = buffer_get_modifyable_data(buf, &size);
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch for (i = 0; i < size; i += record_size) {
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch seq = (uint32_t *)&data[i];
14bd2410de3a0261d9c53c6120915027262216bdTimo Sirainen
14bd2410de3a0261d9c53c6120915027262216bdTimo Sirainen for (j = 0; j <= range; j++, seq++) {
14bd2410de3a0261d9c53c6120915027262216bdTimo Sirainen if (*seq >= t->first_new_seq) {
9f8cef4cbc49797053c343209ea13022fdbc5a63Stephan Bosch rec = mail_index_transaction_lookup(t, *seq);
9f8cef4cbc49797053c343209ea13022fdbc5a63Stephan Bosch *seq = rec->uid;
faa8995f1d300e7a8917407a52bbd1b98e10bf25Timo Sirainen } else {
9f8cef4cbc49797053c343209ea13022fdbc5a63Stephan Bosch i_assert(*seq <= view->map->records_count);
faa8995f1d300e7a8917407a52bbd1b98e10bf25Timo Sirainen *seq = MAIL_INDEX_MAP_IDX(view->map,
faa8995f1d300e7a8917407a52bbd1b98e10bf25Timo Sirainen *seq - 1)->uid;
faa8995f1d300e7a8917407a52bbd1b98e10bf25Timo Sirainen }
faa8995f1d300e7a8917407a52bbd1b98e10bf25Timo Sirainen i_assert(*seq != 0);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschstatic int
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschmail_index_transaction_convert_to_uids(struct mail_index_transaction *t)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch struct mail_index *index = t->view->index;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch const struct mail_index_extra_record_info *einfos;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch buffer_t **updates;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size_t i, size;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (mail_index_view_lock(t->view) < 0)
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch return -1;
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch
833bed942977673526c72e79bccc09314fc57104Phil Carmody if (t->extra_rec_updates != NULL) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch einfos = buffer_get_data(index->extra_infos, NULL);
833bed942977673526c72e79bccc09314fc57104Phil Carmody updates = buffer_get_modifyable_data(t->extra_rec_updates,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch &size);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size /= sizeof(*updates);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch for (i = 0; i < size; i++) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (updates[i] == NULL)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch continue;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
91a482473f200152d6713181c0e36f7a4f03ef6dTimo Sirainen mail_index_buffer_convert_to_uids(t, updates[i],
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch sizeof(uint32_t) + einfos[i].record_size,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch FALSE);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch mail_index_buffer_convert_to_uids(t, t->expunges,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch sizeof(struct mail_transaction_expunge), TRUE);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch mail_index_buffer_convert_to_uids(t, t->updates,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch sizeof(struct mail_transaction_flag_update), TRUE);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch mail_index_buffer_convert_to_uids(t, t->cache_updates,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch sizeof(struct mail_transaction_cache_update), FALSE);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return 0;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
85f3bd5926fff0e70b6d259a5c8074bd8cdeb9adTimo Sirainenint mail_index_transaction_commit(struct mail_index_transaction *t,
85f3bd5926fff0e70b6d259a5c8074bd8cdeb9adTimo Sirainen uint32_t *log_file_seq_r,
85f3bd5926fff0e70b6d259a5c8074bd8cdeb9adTimo Sirainen uoff_t *log_file_offset_r)
85f3bd5926fff0e70b6d259a5c8074bd8cdeb9adTimo Sirainen{
85f3bd5926fff0e70b6d259a5c8074bd8cdeb9adTimo Sirainen int ret;
85f3bd5926fff0e70b6d259a5c8074bd8cdeb9adTimo Sirainen
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch if (mail_index_view_is_inconsistent(t->view)) {
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch mail_index_transaction_rollback(t);
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch return -1;
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch }
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch if (t->cache_trans_ctx != NULL) {
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch mail_cache_transaction_commit(t->cache_trans_ctx);
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch t->cache_trans_ctx = NULL;
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch }
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch if (t->last_update.uid1 != 0)
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch mail_index_transaction_add_last(t);
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch if (mail_index_transaction_convert_to_uids(t) < 0)
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch ret = -1;
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch else {
93ed69606237a08623f8294c060fa148880058f8Timo Sirainen ret = mail_transaction_log_append(t, log_file_seq_r,
93ed69606237a08623f8294c060fa148880058f8Timo Sirainen log_file_offset_r);
93ed69606237a08623f8294c060fa148880058f8Timo Sirainen }
93ed69606237a08623f8294c060fa148880058f8Timo Sirainen
93ed69606237a08623f8294c060fa148880058f8Timo Sirainen mail_index_transaction_unref(t);
93ed69606237a08623f8294c060fa148880058f8Timo Sirainen return ret;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschvoid mail_index_transaction_rollback(struct mail_index_transaction *t)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (t->cache_trans_ctx != NULL) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch mail_cache_transaction_rollback(t->cache_trans_ctx);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch t->cache_trans_ctx = NULL;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch mail_index_transaction_unref(t);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
711e8e4c5c5d702dfa062f42a1ede5de14c151c9Stephan Boschstruct mail_index_record *
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschmail_index_transaction_lookup(struct mail_index_transaction *t, uint32_t seq)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size_t pos;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_assert(seq >= t->first_new_seq && seq <= t->last_new_seq);
711e8e4c5c5d702dfa062f42a1ede5de14c151c9Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch pos = (seq - t->first_new_seq) * sizeof(struct mail_index_record);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return buffer_get_space_unsafe(t->appends, pos,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch sizeof(struct mail_index_record));
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschvoid mail_index_append(struct mail_index_transaction *t, uint32_t uid,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch uint32_t *seq_r)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch struct mail_index_record *rec;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch t->log_updates = TRUE;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (t->appends == NULL) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch t->appends = buffer_create_dynamic(default_pool,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch 4096, (size_t)-1);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch /* sequence number is visible only inside given view,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch so let it generate it */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (t->last_new_seq != 0)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch *seq_r = ++t->last_new_seq;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch else
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch *seq_r = t->last_new_seq = t->first_new_seq;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch rec = buffer_append_space_unsafe(t->appends, sizeof(*rec));
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch memset(rec, 0, sizeof(*rec));
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch rec->uid = uid;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Boschvoid mail_index_append_assign_uids(struct mail_index_transaction *t,
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch uint32_t first_uid, uint32_t *next_uid_r)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch struct mail_index_record *rec, *end;
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch size_t size;
2f64a4c88de91c483fb378bc80d10e1caa6f2305Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (t->appends == NULL)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
833bed942977673526c72e79bccc09314fc57104Phil Carmody rec = buffer_get_modifyable_data(t->appends, &size);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch end = PTR_OFFSET(rec, size);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch /* find the first mail with uid = 0 */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch for (; rec != end; rec++) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (rec->uid == 0)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch break;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch for (; rec != end; rec++) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_assert(rec->uid == 0);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch rec->uid = first_uid++;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
faa8995f1d300e7a8917407a52bbd1b98e10bf25Timo Sirainen
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch *next_uid_r = first_uid;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch}
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
faa8995f1d300e7a8917407a52bbd1b98e10bf25Timo Sirainenvoid mail_index_expunge(struct mail_index_transaction *t, uint32_t seq)
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch{
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch struct mail_transaction_expunge exp, *data;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch unsigned int idx, left_idx, right_idx;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size_t size;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
711e8e4c5c5d702dfa062f42a1ede5de14c151c9Stephan Bosch i_assert(seq > 0 && seq <= mail_index_view_get_message_count(t->view));
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch t->log_updates = TRUE;
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch exp.uid1 = exp.uid2 = seq;
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch /* expunges is a sorted array of {seq1, seq2, ..}, .. */
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (t->expunges == NULL) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch t->expunges = buffer_create_dynamic(default_pool,
b37e11d37fb1ebf50511eef5d9d96d1205818458Stephan Bosch 1024, (size_t)-1);
b37e11d37fb1ebf50511eef5d9d96d1205818458Stephan Bosch buffer_append(t->expunges, &exp, sizeof(exp));
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch return;
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch data = buffer_get_modifyable_data(t->expunges, &size);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch size /= sizeof(*data);
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch i_assert(size > 0);
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch /* quick checks */
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch if (data[size-1].uid2 == seq-1) {
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch /* grow last range */
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch data[size-1].uid2 = seq;
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch return;
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch }
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch if (data[size-1].uid2 < seq) {
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch buffer_append(t->expunges, &exp, sizeof(exp));
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch return;
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch }
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch if (data[0].uid1 == seq+1) {
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen /* grow down first range */
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen data[0].uid1 = seq;
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen return;
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen }
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch if (data[0].uid1 > seq) {
3fcb3d2d1f3583025ff62bae95ec706920f398b1Stephan Bosch buffer_insert(t->expunges, 0, &exp, sizeof(exp));
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen return;
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen }
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen /* somewhere in the middle, array is sorted so find it with
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen binary search */
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen idx = 0; left_idx = 0; right_idx = size;
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen while (left_idx < right_idx) {
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen idx = (left_idx + right_idx) / 2;
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen if (data[idx].uid1 < seq)
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen left_idx = idx+1;
95e0b82fdff1bb511067d703bb8b67c22f242c38Timo Sirainen else if (data[idx].uid1 > seq)
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch right_idx = idx;
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch else
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch break;
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch }
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch if (data[idx].uid2 < seq)
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch idx++;
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch /* idx == size couldn't happen because we already handle it above */
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch i_assert(idx < size && data[idx].uid1 >= seq);
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch if (data[idx].uid1 <= seq && data[idx].uid2 >= seq) {
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch /* already expunged */
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch return;
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch }
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch if (data[idx].uid1 == seq+1) {
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch data[idx].uid1 = seq;
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch if (idx > 0 && data[idx-1].uid2 == seq-1) {
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch /* merge */
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch data[idx-1].uid2 = data[idx].uid2;
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch buffer_delete(t->expunges, idx * sizeof(*data),
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch sizeof(*data));
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch }
564e117d86ce5b659f9b9570edddc566f9ebb5dfStephan Bosch } else if (data[idx].uid2 == seq-1) {
i_assert(idx+1 < size); /* already handled above */
data[idx].uid2 = seq;
if (data[idx+1].uid1 == seq+1) {
/* merge */
data[idx+1].uid1 = data[idx].uid1;
buffer_delete(t->expunges, idx * sizeof(*data),
sizeof(*data));
}
} else {
buffer_insert(t->expunges, idx * sizeof(*data),
&exp, sizeof(exp));
}
}
static void mail_index_record_modify_flags(struct mail_index_record *rec,
enum modify_type modify_type,
enum mail_flags flags,
keywords_mask_t keywords)
{
int i;
switch (modify_type) {
case MODIFY_REPLACE:
rec->flags = flags;
memcpy(rec->keywords, keywords, INDEX_KEYWORDS_BYTE_COUNT);
break;
case MODIFY_ADD:
rec->flags |= flags;
for (i = 0; i < INDEX_KEYWORDS_BYTE_COUNT; i++)
rec->keywords[i] |= keywords[i];
break;
case MODIFY_REMOVE:
rec->flags &= ~flags;
for (i = 0; i < INDEX_KEYWORDS_BYTE_COUNT; i++)
rec->keywords[i] &= ~keywords[i];
break;
}
}
#define IS_COMPATIBLE_UPDATE(t, modify_type, flags, keywords) \
((t)->last_update_modify_type == (modify_type) && \
(t)->last_update.add_flags == (flags) && \
memcmp((t)->last_update.add_keywords, keywords, \
INDEX_KEYWORDS_BYTE_COUNT) == 0)
void mail_index_update_flags(struct mail_index_transaction *t, uint32_t seq,
enum modify_type modify_type,
enum mail_flags flags, keywords_mask_t keywords)
{
struct mail_index_record *rec;
t->log_updates = TRUE;
if (seq >= t->first_new_seq) {
/* just appended message, modify it directly */
rec = mail_index_transaction_lookup(t, seq);
mail_index_record_modify_flags(rec, modify_type,
flags, keywords);
return;
}
i_assert(seq > 0 && seq <= mail_index_view_get_message_count(t->view));
/* first get group updates into same structure. this allows faster
updates if same mails have multiple flag updates during same
transaction (eg. 1:10 +seen, 1:10 +deleted) */
if (t->last_update.uid2 == seq-1) {
if (t->last_update.uid1 != 0 &&
IS_COMPATIBLE_UPDATE(t, modify_type, flags, keywords)) {
t->last_update.uid2 = seq;
return;
}
} else if (t->last_update.uid1 == seq+1) {
if (t->last_update.uid1 != 0 &&
IS_COMPATIBLE_UPDATE(t, modify_type, flags, keywords)) {
t->last_update.uid1 = seq;
return;
}
}
if (t->last_update.uid1 != 0)
mail_index_transaction_add_last(t);
t->last_update_modify_type = modify_type;
t->last_update.uid1 = t->last_update.uid2 = seq;
t->last_update.add_flags = flags;
memcpy(t->last_update.add_keywords, keywords,
INDEX_KEYWORDS_BYTE_COUNT);
}
static void
mail_index_transaction_get_last(struct mail_index_transaction *t,
struct mail_transaction_flag_update *update)
{
int i;
*update = t->last_update;
switch (t->last_update_modify_type) {
case MODIFY_REPLACE:
/* remove_flags = ~add_flags */
update->remove_flags =
~update->add_flags & MAIL_INDEX_FLAGS_MASK;
for (i = 0; i < INDEX_KEYWORDS_BYTE_COUNT; i++)
update->remove_keywords[i] = ~update->add_keywords[i];
break;
case MODIFY_ADD:
/* already in add_flags */
break;
case MODIFY_REMOVE:
/* add_flags -> remove_flags */
update->remove_flags = update->add_flags;
memcpy(&update->remove_keywords, &update->add_keywords,
INDEX_KEYWORDS_BYTE_COUNT);
update->add_flags = 0;
memset(&update->add_keywords, 0, INDEX_KEYWORDS_BYTE_COUNT);
break;
}
}
static void mail_index_transaction_add_last(struct mail_index_transaction *t)
{
struct mail_transaction_flag_update update, *data;
unsigned int idx, left_idx, right_idx;
uint32_t last;
size_t size;
mail_index_transaction_get_last(t, &update);
if (t->updates == NULL) {
t->updates = buffer_create_dynamic(default_pool,
4096, (size_t)-1);
}
data = buffer_get_modifyable_data(t->updates, &size);
size /= sizeof(*data);
/* find the nearest sequence from existing updates */
idx = 0; left_idx = 0; right_idx = size;
while (left_idx < right_idx) {
idx = (left_idx + right_idx) / 2;
if (data[idx].uid1 < update.uid1)
left_idx = idx+1;
else if (data[idx].uid1 > update.uid1)
right_idx = idx;
else
break;
}
if (idx < size && data[idx].uid2 < update.uid1)
idx++;
i_assert(idx == size || data[idx].uid1 <= update.uid1);
/* insert it into buffer, split it in multiple parts if needed
to make sure the ordering stays the same */
for (; idx < size; idx++) {
if (data[idx].uid1 > update.uid2)
break;
/* partial */
last = update.uid2;
update.uid2 = data[idx].uid1-1;
if (update.uid1 <= update.uid2) {
buffer_insert(t->updates, idx * sizeof(update),
&update, sizeof(update));
data = buffer_get_modifyable_data(t->updates, NULL);
size++;
}
update.uid1 = update.uid2+1;
update.uid2 = last;
}
buffer_insert(t->updates, idx * sizeof(update),
&update, sizeof(update));
}
int mail_index_seq_buffer_lookup(buffer_t *buffer, uint32_t seq,
size_t record_size, size_t *pos_r)
{
unsigned int idx, left_idx, right_idx;
void *data;
uint32_t full_record_size, *seq_p;
size_t size;
full_record_size = record_size + sizeof(seq);
data = buffer_get_modifyable_data(buffer, &size);
/* we're probably appending it, check */
if (size == 0)
idx = 0;
else if (*((uint32_t *)PTR_OFFSET(data, size-full_record_size)) < seq)
idx = size / full_record_size;
else {
idx = 0; left_idx = 0; right_idx = size / full_record_size;
while (left_idx < right_idx) {
idx = (left_idx + right_idx) / 2;
seq_p = PTR_OFFSET(data, idx * full_record_size);
if (*seq_p < seq)
left_idx = idx+1;
else if (*seq_p > seq)
right_idx = idx;
else {
*pos_r = idx * full_record_size;
return TRUE;
}
}
}
*pos_r = idx * full_record_size;
return FALSE;
}
static int mail_index_update_seq_buffer(buffer_t **buffer, uint32_t seq,
const void *record, size_t record_size,
void *old_record)
{
void *p;
size_t pos;
if (*buffer == NULL) {
*buffer = buffer_create_dynamic(default_pool, 1024, (size_t)-1);
buffer_append(*buffer, &seq, sizeof(seq));
buffer_append(*buffer, record, record_size);
return FALSE;
}
if (mail_index_seq_buffer_lookup(*buffer, seq, record_size, &pos)) {
/* already there, update */
p = buffer_get_space_unsafe(*buffer, pos + sizeof(seq),
record_size);
if (old_record != NULL)
memcpy(old_record, p, record_size);
memcpy(p, record, record_size);
return TRUE;
} else {
/* insert */
buffer_copy(*buffer, pos + sizeof(seq) + record_size,
*buffer, pos, (size_t)-1);
buffer_write(*buffer, pos, &seq, sizeof(seq));
buffer_write(*buffer, pos + sizeof(seq), record, record_size);
return FALSE;
}
}
static void
mail_index_transaction_reset_cache_updates(struct mail_index_transaction *t)
{
struct mail_index_record *rec;
uint32_t seq;
if (t->last_cache_file_seq == 0)
return;
buffer_set_used_size(t->cache_updates, 0);
if (t->first_new_seq != 0) {
for (seq = t->first_new_seq; seq <= t->last_new_seq; seq++) {
rec = mail_index_transaction_lookup(t, seq);
rec->cache_offset = 0;
}
}
}
void mail_index_reset_cache(struct mail_index_transaction *t,
uint32_t new_file_seq)
{
t->log_updates = TRUE;
mail_index_transaction_reset_cache_updates(t);
t->new_cache_file_seq = new_file_seq;
t->last_cache_file_seq = new_file_seq;
}
void mail_index_update_cache(struct mail_index_transaction *t, uint32_t seq,
uint32_t file_seq, uint32_t offset,
uint32_t *old_offset_r)
{
struct mail_index_record *rec;
i_assert(seq > 0 &&
(seq <= mail_index_view_get_message_count(t->view) ||
seq <= t->last_new_seq));
t->log_updates = TRUE;
if (file_seq != t->last_cache_file_seq) {
mail_index_transaction_reset_cache_updates(t);
t->last_cache_file_seq = file_seq;
}
if (seq >= t->first_new_seq) {
/* just appended message, modify it directly */
rec = mail_index_transaction_lookup(t, seq);
*old_offset_r = rec->cache_offset;
rec->cache_offset = offset;
} else {
if (!mail_index_update_seq_buffer(&t->cache_updates, seq,
&offset, sizeof(offset),
old_offset_r))
*old_offset_r = 0;
}
}
int mail_index_update_cache_lookup(struct mail_index_transaction *t,
uint32_t seq, uint32_t *offset_r)
{
const void *p;
size_t pos;
i_assert(seq > 0 &&
(seq <= mail_index_view_get_message_count(t->view) ||
seq <= t->last_new_seq));
if (t->cache_updates == NULL)
return FALSE;
if (MAIL_CACHE_IS_UNUSABLE(t->view->index->cache) ||
t->view->index->cache->hdr->file_seq != t->last_cache_file_seq) {
/* cache file was recreated, our offsets don't work anymore */
mail_index_transaction_reset_cache_updates(t);
t->last_cache_file_seq = 0;
return FALSE;
}
if (!mail_index_seq_buffer_lookup(t->cache_updates, seq,
sizeof(*offset_r), &pos))
return FALSE;
p = buffer_get_data(t->cache_updates, NULL);
memcpy(offset_r, CONST_PTR_OFFSET(p, pos + sizeof(*offset_r)),
sizeof(*offset_r));
return TRUE;
}
void mail_index_update_extra_rec(struct mail_index_transaction *t,
uint32_t seq, uint32_t data_id,
const void *data)
{
struct mail_index *index = t->view->index;
buffer_t **buf;
const struct mail_index_extra_record_info *einfo;
i_assert(seq > 0 &&
(seq <= mail_index_view_get_message_count(t->view) ||
seq <= t->last_new_seq));
i_assert(data_id < index->extra_infos->used / sizeof(*einfo));
t->log_updates = TRUE;
einfo = index->extra_infos->data;
einfo += data_id;
if (t->extra_rec_updates == NULL) {
t->extra_rec_updates =
buffer_create_dynamic(default_pool, 128, (size_t)-1);
}
buf = buffer_get_space_unsafe(t->extra_rec_updates,
data_id * sizeof(buffer_t *),
sizeof(buffer_t *));
mail_index_update_seq_buffer(buf, seq, data, einfo->record_size, NULL);
}
void mail_index_update_header(struct mail_index_transaction *t,
size_t offset, const void *data, size_t size)
{
i_assert(offset < sizeof(t->hdr_change));
i_assert(size <= sizeof(t->hdr_change) - offset);
t->hdr_changed = TRUE;
t->log_updates = TRUE;
memcpy(t->hdr_change + offset, data, size);
for (; size > 0; size--)
t->hdr_mask[offset++] = 1;
}