struct dbox_file *open_file;
uoff_t offset;
+ uint32_t map_uid;
};
struct mail *
return 0;
}
+static void dbox_mail_set_expunged(struct dbox_mail *mail)
+{
+ struct dbox_mailbox *mbox = (struct dbox_mailbox *)mail->imail.ibox;
+ struct mail *_mail = &mail->imail.mail.mail;
+
+ (void)mail_index_refresh(mbox->ibox.index);
+ if (mail_index_is_expunged(mbox->ibox.view, _mail->seq)) {
+ mail_set_expunged(_mail);
+ return;
+ }
+
+ if (mail->map_uid != 0) {
+ dbox_map_set_corrupted(mbox->storage->map,
+ "Unexpectedly lost uid=%u map_uid=%u",
+ _mail->uid, mail->map_uid);
+ } else {
+ mail_storage_set_critical(&mbox->storage->storage,
+ "Unexpectedly lost uid=%u", _mail->uid);
+ }
+ mbox->storage->sync_rebuild = TRUE;
+}
+
static int dbox_mail_open(struct dbox_mail *mail,
uoff_t *offset_r, struct dbox_file **file_r)
{
struct dbox_mailbox *mbox = (struct dbox_mailbox *)mail->imail.ibox;
struct mail *_mail = &mail->imail.mail.mail;
- uint32_t map_uid, file_id;
+ uint32_t file_id;
int ret;
if (mail->open_file == NULL) {
if (dbox_mail_lookup(mbox, mbox->ibox.view, _mail->seq,
- &map_uid) < 0)
+ &mail->map_uid) < 0)
return -1;
- if (map_uid == 0) {
+ if (mail->map_uid == 0) {
mail->open_file =
dbox_file_init_single(mbox, _mail->uid);
} else if ((ret = dbox_map_lookup(mbox->storage->map,
- map_uid, &file_id,
+ mail->map_uid, &file_id,
&mail->offset)) <= 0) {
if (ret < 0)
return -1;
/* map_uid doesn't exist anymore. either it
got just expunged or the map index is
- corrupted. see if the message still exists
- in the mailbox index. */
- (void)mail_index_refresh(mbox->ibox.index);
- if (mail_index_is_expunged(mbox->ibox.view,
- _mail->seq)) {
- mail_set_expunged(_mail);
- return -1;
- }
-
- dbox_map_set_corrupted(mbox->storage->map,
- "Unexpectedly lost map_uid=%u", map_uid);
- mbox->storage->sync_rebuild = TRUE;
+ corrupted. */
+ dbox_mail_set_expunged(mail);
return -1;
} else {
mail->open_file =
static int
dbox_mail_metadata_read(struct dbox_mail *mail, struct dbox_file **file_r)
{
- struct mail *_mail = &mail->imail.mail.mail;
uoff_t offset, size;
bool expunged;
NULL, &expunged) <= 0)
return -1;
if (expunged) {
- mail_set_expunged(_mail);
+ dbox_mail_set_expunged(mail);
return -1;
}
if (dbox_file_metadata_read(*file_r) <= 0)
return -1;
}
if (expunged) {
- mail_set_expunged(_mail);
+ dbox_mail_set_expunged(mail);
return -1;
}
data->physical_size = size;
struct dbox_map_transaction_context {
struct dbox_map *map;
struct mail_index_transaction *trans;
+ struct mail_index_sync_ctx *sync_ctx;
+
unsigned int changed:1;
+ unsigned int success:1;
};
void dbox_map_set_corrupted(struct dbox_map *map, const char *format, ...)
}
}
-int dbox_map_transaction_commit(struct dbox_map_transaction_context **_ctx)
+int dbox_map_transaction_commit(struct dbox_map_transaction_context *ctx)
{
- struct dbox_map_transaction_context *ctx = *_ctx;
struct dbox_map *map = ctx->map;
- struct mail_index_sync_ctx *sync_ctx;
struct mail_index_view *view;
- struct mail_index_transaction *sync_trans, *trans = ctx->trans;
+ struct mail_index_transaction *sync_trans;
int ret;
- *_ctx = NULL;
- if (!ctx->changed) {
- mail_index_transaction_rollback(&trans);
- i_free(ctx);
+ if (!ctx->changed)
return 0;
- }
- i_free(ctx);
/* use syncing to lock the transaction log, so that we always see
log's head_offset = tail_offset */
- ret = mail_index_sync_begin(map->index, &sync_ctx,
+ ret = mail_index_sync_begin(map->index, &ctx->sync_ctx,
&view, &sync_trans, 0);
if (ret <= 0) {
i_assert(ret != 0);
mail_storage_set_internal_error(&map->storage->storage);
mail_index_reset_error(map->index);
- mail_index_transaction_rollback(&trans);
+ mail_index_transaction_rollback(&ctx->trans);
return -1;
}
- dbox_map_sync_handle(map, sync_ctx);
+ dbox_map_sync_handle(map, ctx->sync_ctx);
- if (mail_index_transaction_commit(&trans) < 0 ||
- mail_index_sync_commit(&sync_ctx) < 0) {
+ if (mail_index_transaction_commit(&ctx->trans) < 0) {
mail_storage_set_internal_error(&map->storage->storage);
mail_index_reset_error(map->index);
- if (sync_ctx != NULL)
- mail_index_sync_rollback(&sync_ctx);
return -1;
}
+ ctx->success = TRUE;
return 0;
}
-void dbox_map_transaction_rollback(struct dbox_map_transaction_context **_ctx)
+void dbox_map_transaction_free(struct dbox_map_transaction_context **_ctx)
{
struct dbox_map_transaction_context *ctx = *_ctx;
+ struct dbox_map *map = ctx->map;
*_ctx = NULL;
- mail_index_transaction_rollback(&ctx->trans);
+ if (ctx->success) {
+ if (mail_index_sync_commit(&ctx->sync_ctx) < 0) {
+ mail_storage_set_internal_error(&map->storage->storage);
+ mail_index_reset_error(map->index);
+ }
+ } else if (ctx->sync_ctx != NULL) {
+ mail_index_sync_rollback(&ctx->sync_ctx);
+ }
+ if (ctx->trans != NULL)
+ mail_index_transaction_rollback(&ctx->trans);
i_free(ctx);
}
int dbox_map_update_refcounts(struct dbox_map_transaction_context *ctx,
- const ARRAY_TYPE(seq_range) *map_uids, int diff)
+ const ARRAY_TYPE(uint32_t) *map_uids, int diff)
{
struct dbox_map *map = ctx->map;
- struct seq_range_iter iter;
- unsigned int i;
- uint32_t map_uid, seq;
+ const uint32_t *uids;
+ unsigned int i, count;
+ uint32_t seq;
int ret;
- seq_range_array_iter_init(&iter, map_uids); i = 0;
- while (seq_range_array_iter_nth(&iter, i++, &map_uid)) {
- if ((ret = dbox_map_get_seq(map, map_uid, &seq)) <= 0) {
+ uids = array_get(map_uids, &count);
+ for (i = 0; i < count; i++) {
+ if ((ret = dbox_map_get_seq(map, uids[i], &seq)) <= 0) {
if (ret == 0) {
dbox_map_set_corrupted(map,
"refcount update lost map_uid=%u",
- map_uid);
+ uids[i]);
}
return -1;
}
mail_index_expunge(map_trans->trans, seq);
}
}
- if (ret < 0)
- dbox_map_transaction_rollback(&map_trans);
- else
- (void)dbox_map_transaction_commit(&map_trans);
+ if (ret == 0)
+ (void)dbox_map_transaction_commit(map_trans);
+ dbox_map_transaction_free(&map_trans);
return ret;
}
int dbox_map_append_commit(struct dbox_map_append_context *ctx)
{
- i_assert(ctx->sync_ctx != NULL);
+ struct dbox_map *map = ctx->map;
+
i_assert(ctx->trans == NULL);
- if (mail_index_sync_commit(&ctx->sync_ctx) < 0) {
- mail_storage_set_internal_error(&ctx->map->storage->storage);
- mail_index_reset_error(ctx->map->index);
- return -1;
+ if (ctx->sync_ctx != NULL) {
+ if (mail_index_sync_commit(&ctx->sync_ctx) < 0) {
+ mail_storage_set_internal_error(&map->storage->storage);
+ mail_index_reset_error(map->index);
+ return -1;
+ }
}
ctx->committed = TRUE;
return 0;
struct dbox_map_transaction_context *
dbox_map_transaction_begin(struct dbox_map *map, bool external);
-int dbox_map_transaction_commit(struct dbox_map_transaction_context **ctx);
-void dbox_map_transaction_rollback(struct dbox_map_transaction_context **ctx);
+/* Write transaction to map and leave it locked. Call _free() to update tail
+ offset and unlock. */
+int dbox_map_transaction_commit(struct dbox_map_transaction_context *ctx);
+void dbox_map_transaction_free(struct dbox_map_transaction_context **ctx);
int dbox_map_update_refcounts(struct dbox_map_transaction_context *ctx,
- const ARRAY_TYPE(seq_range) *map_uids, int diff);
+ const ARRAY_TYPE(uint32_t) *map_uids, int diff);
int dbox_map_remove_file_id(struct dbox_map *map, uint32_t file_id);
/* Return all files containing messages with zero refcount. */
#include "ostream.h"
#include "write-full.h"
#include "index-mail.h"
+#include "mail-copy.h"
#include "dbox-storage.h"
#include "dbox-map.h"
#include "dbox-file.h"
struct dbox_map_append_context *append_ctx;
struct dbox_sync_context *sync_ctx;
+ ARRAY_TYPE(uint32_t) copy_map_uids;
+ struct dbox_map_transaction_context *map_trans;
+
/* updated for each appended mail: */
uint32_t seq;
struct istream *input;
i_assert((t->ictx.flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
- if (t->save_ctx != NULL)
+ if (t->save_ctx != NULL) {
+ /* use the existing allocated structure */
+ t->save_ctx->finished = FALSE;
return &t->save_ctx->ctx;
+ }
ctx = t->save_ctx = i_new(struct dbox_save_context, 1);
ctx->ctx.transaction = &t->ictx.mailbox_ctx;
return &ctx->ctx;
}
+static void dbox_save_add_to_index(struct dbox_save_context *ctx)
+{
+ enum mail_flags save_flags;
+
+ save_flags = ctx->ctx.flags & ~MAIL_RECENT;
+ mail_index_append(ctx->trans, 0, &ctx->seq);
+ mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE,
+ save_flags);
+ if (ctx->ctx.keywords != NULL) {
+ mail_index_update_keywords(ctx->trans, ctx->seq,
+ MODIFY_REPLACE, ctx->ctx.keywords);
+ }
+}
+
int dbox_save_begin(struct mail_save_context *_ctx, struct istream *input)
{
struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
struct dbox_message_header dbox_msg_hdr;
struct dbox_save_mail *save_mail;
struct istream *crlf_input;
- enum mail_flags save_flags;
uoff_t mail_size;
/* get the size of the mail to be saved, if possible */
return -1;
}
- /* add to index */
- save_flags = _ctx->flags & ~MAIL_RECENT;
- mail_index_append(ctx->trans, 0, &ctx->seq);
- mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE,
- save_flags);
- if (_ctx->keywords != NULL) {
- mail_index_update_keywords(ctx->trans, ctx->seq,
- MODIFY_REPLACE, _ctx->keywords);
- }
+ dbox_save_add_to_index(ctx);
if (_ctx->dest_mail == NULL) {
if (ctx->mail == NULL)
i_assert(next_map_uid == last_map_uid + 1);
}
+ /* increase map's refcount for copied mails */
+ if (array_is_created(&ctx->copy_map_uids)) {
+ ctx->map_trans =
+ dbox_map_transaction_begin(ctx->mbox->storage->map,
+ FALSE);
+ if (dbox_map_update_refcounts(ctx->map_trans,
+ &ctx->copy_map_uids, 1) < 0) {
+ dbox_transaction_save_rollback(ctx);
+ return -1;
+ }
+ }
+
if (ctx->mail != NULL)
mail_free(&ctx->mail);
/* finish writing the mailbox APPENDs */
if (dbox_sync_finish(&ctx->sync_ctx, TRUE) == 0) {
+ if (ctx->map_trans != NULL)
+ (void)dbox_map_transaction_commit(ctx->map_trans);
/* commit only updates the sync tail offset, everything else
was already written at this point. */
(void)dbox_map_append_commit(ctx->append_ctx);
dbox_save_cancel(&ctx->ctx);
if (ctx->append_ctx != NULL)
dbox_map_append_free(&ctx->append_ctx);
+ if (ctx->map_trans != NULL)
+ dbox_map_transaction_free(&ctx->map_trans);
+ if (array_is_created(&ctx->copy_map_uids))
+ array_free(&ctx->copy_map_uids);
if (ctx->sync_ctx != NULL)
(void)dbox_sync_finish(&ctx->sync_ctx, FALSE);
array_free(&ctx->mails);
i_free(ctx);
}
+
+int dbox_copy(struct mail_save_context *_ctx, struct mail *mail)
+{
+ struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx;
+ struct dbox_mailbox *src_mbox;
+ struct dbox_mail_index_record rec;
+ const void *data;
+ bool expunged;
+
+ ctx->finished = TRUE;
+
+ if (mail->box->storage != _ctx->transaction->box->storage)
+ return mail_storage_copy(_ctx, mail);
+ src_mbox = (struct dbox_mailbox *)mail->box;
+
+ memset(&rec, 0, sizeof(rec));
+ if (dbox_mail_lookup(src_mbox, src_mbox->ibox.view, mail->seq,
+ &rec.map_uid) < 0)
+ return -1;
+
+ if (rec.map_uid == 0) {
+ /* FIXME: we could hard link */
+ return mail_storage_copy(_ctx, mail);
+ }
+
+ /* remember the map_uid so we can later increase its refcount */
+ if (!array_is_created(&ctx->copy_map_uids))
+ i_array_init(&ctx->copy_map_uids, 32);
+ array_append(&ctx->copy_map_uids, &rec.map_uid, 1);
+
+ /* add message to mailbox index */
+ dbox_save_add_to_index(ctx);
+ mail_index_update_ext(ctx->trans, ctx->seq, ctx->mbox->dbox_ext_id,
+ &rec, NULL);
+
+ mail_index_lookup_ext(src_mbox->ibox.view, mail->seq,
+ src_mbox->guid_ext_id, &data, &expunged);
+ if (data != NULL) {
+ mail_index_update_ext(ctx->trans, ctx->seq,
+ ctx->mbox->guid_ext_id, data, NULL);
+ }
+ return 0;
+}
dbox_save_continue,
dbox_save_finish,
dbox_save_cancel,
- mail_storage_copy,
+ dbox_copy,
index_storage_is_inconsistent
}
};
void dbox_transaction_save_commit_post(struct dbox_save_context *ctx);
void dbox_transaction_save_rollback(struct dbox_save_context *ctx);
+int dbox_copy(struct mail_save_context *ctx, struct mail *mail);
+
#endif
lookup_entry.uid != 0 ? 1 : 3);
}
seq_range_array_add(&entry->expunge_seqs, 0, seq);
- seq_range_array_add(&entry->expunge_map_uids, 0, map_uid);
+ array_append(&entry->expunge_map_uids, &map_uid, 1);
} else {
if ((sync_rec->add_flags & DBOX_INDEX_FLAG_ALT) != 0)
entry->move_to_alt = TRUE;
}
if (ret > 0 && ctx->map_trans != NULL) {
- if (dbox_map_transaction_commit(&ctx->map_trans) < 0)
+ if (dbox_map_transaction_commit(ctx->map_trans) < 0)
ret = -1;
+ dbox_map_transaction_free(&ctx->map_trans);
}
if (box->v.sync_notify != NULL)
*_ctx = NULL;
if (ctx->map_trans != NULL)
- dbox_map_transaction_rollback(&ctx->map_trans);
+ dbox_map_transaction_free(&ctx->map_trans);
if (success) {
if (mail_index_sync_commit(&ctx->index_sync_ctx) < 0) {
unsigned int move_from_alt:1;
unsigned int move_to_alt:1;
ARRAY_TYPE(seq_range) expunge_seqs;
- ARRAY_TYPE(seq_range) expunge_map_uids;
+ ARRAY_TYPE(uint32_t) expunge_map_uids;
};
struct dbox_sync_context {