mbox-sync.c revision e06c0b65c16ccce69bbee009ead14d7d3d17a256
/* Copyright (C) 2004 Timo Sirainen */
/*
Modifying mbox can be slow, so we try to do it all at once minimizing the
required disk I/O. We may need to:
- Update message flags in Status, X-Status and X-Keywords headers
- Write missing X-UID and X-IMAPbase headers
- Write missing or broken Content-Length header if there's space
- Expunge specified messages
Here's how we do it:
- Start reading the mails mail headers from the beginning
- X-Keywords and X-UID headers may contain extra spaces at the end of them,
remember how much extra each message has and offset to beginning of the
spaces
- If message flags are dirty and there's enough space to write them, do it
- If we didn't have enough space, remember how much was missing and keep
the total amount of them
- When we encounter expunged message, check if the amount of empty space in
previous messages plus size of expunged message is enough to cover the
missing space. If yes,
- execute the rewrite plan
- forget all the messages before the expunged message. only remember
how much data we still have to move to cover the expunged message
- If we encounter end of file, grow the file and execute the rewrite plan
Rewrite plan goes:
- Start from the first message that needs more space
- If there's expunged messages before us, we have to write over them.
- Move all messages after it backwards to fill it
- Each moved message's X-Keywords header should have n bytes extra
space, unless there's not enough space to do it.
- If there's no expunged messages, we can move data either forward or
backward to get it. Calculate which requires less moving. Forward
counting may encounter more messages which require extra space, count
that too.
- If we decide to move forwards and we had to go through dirty
messages, do the moving from last to first dirty message
- If we encounter end of file, grow the file enough to get the required
amount of space plus enough space to fill X-Keywords headers full of
spaces.
*/
#include "lib.h"
#include "ioloop.h"
#include "buffer.h"
#include "istream.h"
#include "file-set-size.h"
#include "str.h"
#include "write-full.h"
#include "istream-raw-mbox.h"
#include "mbox-storage.h"
#include "mbox-file.h"
#include "mbox-lock.h"
#include "mbox-sync-private.h"
#include <stddef.h>
struct mbox_sync_mail_context *mail_ctx,
{
/* put the extra space between last message's header and body */
return -1;
return -1;
return 0;
}
{
struct mail_index_sync_rec *sync;
dest++;
}
}
}
static void
{
/* save the offset permanently with recent flag state */
from_offset <<= 1;
/* need to add 'O' flag to Status-header */
from_offset |= 1;
}
sizeof(from_offset));
}
{
const struct mail_index_sync_rec *sync;
for (i = 0; i < size; i++)
}
struct mail_index_sync_ctx *index_sync_ctx,
struct mail_index_view *sync_view)
{
struct mbox_sync_context sync_ctx;
struct mbox_sync_mail_context mail_ctx;
struct mail_index_sync_rec sync_rec;
struct mail_index_transaction *t;
const struct mail_index_header *hdr;
const struct mail_index_record *rec;
int ret = 0;
} else {
}
for (seq = 0;;) {
/* set input->eof */
break;
seq++;
/* get all sync records related to this message */
ret = 1;
sizeof(sync_rec));
}
if (ret == 0)
}
if (ret < 0)
break;
ret = -1;
break;
}
}
if ((mail_ctx.need_rewrite ||
ret = -2;
break;
}
return -1;
if (ret == 0 && need_space_seq == 0) {
/* first mail with no space to write it */
space_diff = 0;
}
}
/* update index */
do {
break;
if (idx_seq >= messages_count) {
break;
}
} while (ret == 0);
if (ret < 0)
break;
/* new UID in the middle of the mailbox -
shouldn't happen */
"mbox sync: UID inserted in the middle "
"of mailbox (%u > %u)",
ret = -1;
break;
}
/* see if flags changed */
INDEX_KEYWORDS_BYTE_COUNT) != 0) {
}
/* we used this record */
} else {
/* new message */
}
if (need_space_seq != 0) {
if (space_diff >= 0) {
/* we have enough space now */
space_diff) < 0) {
ret = -1;
break;
}
/* mail_ctx may contain wrong data after
rewrite, so make sure we don't try to access
it */
need_space_seq = 0;
}
}
}
if (need_space_seq != 0) {
i_assert(space_diff < 0);
-space_diff + extra_space) < 0)
ret = -1;
else if (mbox_sync_try_rewrite(&mail_ctx) < 0)
ret = -1;
else if (seq != need_space_seq) {
seq, extra_space) < 0)
ret = -1;
}
}
while (idx_seq < messages_count)
mail_index_expunge(t, ++idx_seq);
// FIXME: rewrite X-IMAPbase header
}
if (ret >= 0) {
/* only syncs left should be just appends which weren't synced
yet. we'll just ignore them, as we've overwritten those
above. */
&sync_rec)) > 0) {
}
}
if (ret == 0) {
ret = -1;
}
}
if (ret < 0) {
}
ret = -1;
&sync_stamp, sizeof(sync_stamp));
}
}
if (ret < 0)
else {
ret = -1;
else if (seq != 0) {
}
}
if (ret != -2) {
if (mail_index_sync_end(index_sync_ctx) < 0)
ret = -1;
}
if (ret == 0) {
ibox->commit_log_file_seq = 0;
ibox->commit_log_file_offset = 0;
} else {
}
}
{
struct mail_index_sync_ctx *index_sync_ctx;
struct mail_index_view *sync_view;
unsigned int lock_id;
if (last_commit) {
} else {
}
if (ret <= 0)
return ret;
mbox_file_open_stream(ibox) == 0) {
if (ret == -2) {
/* read lock -> write lock. do it again. */
lock_id = 0;
ret = -1;
else if (mbox_file_open_stream(ibox) < 0)
ret = -1;
else {
}
}
} else {
(void)mail_index_sync_end(index_sync_ctx);
ret = -1;
}
if (lock_id != 0) {
ret = -1;
}
return ret;
}
{
if ((flags & MAILBOX_SYNC_FLAG_FAST) == 0 ||
return -1;
}
}