mail-index-view-sync.c revision 1f4f81ba81bb9fa8abe2d94f344373c230066d67
/* Copyright (c) 2003-2015 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "buffer.h"
#include "mail-index-view-private.h"
#include "mail-index-sync-private.h"
#include "mail-index-modseq.h"
#include "mail-transaction-log.h"
#include <stdlib.h>
struct mail_index_view_sync_ctx {
struct mail_index_view *view;
struct mail_index_sync_map_ctx sync_map_ctx;
/* After syncing view, map is replaced with sync_new_map. */
struct mail_index_map *sync_new_map;
unsigned int finish_min_msg_count;
const struct mail_transaction_header *hdr;
const void *data;
/* temporary variables while handling lost transaction logs: */
/* result of lost transaction logs: */
unsigned int lost_flag_idx;
unsigned int failed:1;
unsigned int sync_map_update:1;
unsigned int skipped_expunges:1;
unsigned int last_read:1;
unsigned int log_was_lost:1;
unsigned int hidden:1;
};
static int
bool *reset_r)
{
const char *reason;
int ret;
"%s log position went backwards "
return -1;
}
for (;;) {
/* the view begins from the first non-synced transaction */
if (ret <= 0)
return ret;
if (!*reset_r || sync_expunges)
break;
/* log was reset, but we don't want to sync expunges.
we can't do this, so sync only up to the reset. */
&end_seq, &end_offset);
/* we have only this reset log */
break;
}
}
return 1;
}
static unsigned int
{
unsigned int count, expunge_count = 0;
/* convert UIDs to sequences */
count--;
else {
dest++;
}
}
return expunge_count;
}
static void
{
unsigned int i, src_count;
for (i = 0; i < src_count; i++)
}
static void
const struct mail_transaction_expunge_guid *src,
{
unsigned int i, src_count;
for (i = 0; i < src_count; i++)
}
static int
unsigned int *expunge_count_r)
{
const struct mail_transaction_header *hdr;
const void *data;
int ret;
/* get a list of expunge transactions. there may be some that we have
already synced, but it doesn't matter because they'll get dropped
out when converting to sequences. the uid ranges' validity has
already been verified, so we can use them directly. */
/* skip expunge requests */
continue;
}
}
}
return ret;
}
{
return TRUE;
}
return FALSE;
}
static bool
const struct mail_transaction_expunge_guid *expunges,
{
const struct mail_transaction_expunge_guid *expunges_end;
return TRUE;
}
return FALSE;
}
{
const struct mail_transaction_header *hdr;
const void *data;
bool have_expunges = FALSE;
int ret;
/* skip expunge requests */
continue;
}
/* we have an expunge. see if it still exists. */
break;
}
/* we have an expunge. see if it still exists. */
break;
}
}
}
/* handle failures as having expunges (which is safer).
we'll probably fail later. */
return ret < 0 || have_expunges;
}
{
return -1;
return 1;
return 0;
}
{
return FALSE;
}
{
struct mail_transaction_header thdr;
struct mail_transaction_keyword_update kw_up;
const unsigned int *kw_idx;
const char *const *kw_names;
unsigned int i, count;
if (count == 0)
return 0;
/* add new flags one by one */
for (i = 0; i < count; i++) {
}
return -1;
}
return 0;
}
{
struct mail_transaction_header thdr;
const struct mail_index_ext *ext;
/* check this before syncing the record, since it updates
old_rec. */
&flag_update) < 0)
return -1;
}
if (!view_sync_lost_keywords_equal(ctx)) {
/* remove all old flags by resetting them */
&kw_reset) < 0)
return -1;
return -1;
}
if (changed) {
/* flags or keywords changed */
/* if modseq has changed include this message in changed flags
list, even if we didn't see any changes above. */
new_modseq = *modseqp;
}
/* without modseqs lost_flags isn't updated perfectly correctly, because
by the time we're comparing old flags it may have changed from what
we last sent to the client (because the map is shared). This could
be avoided by always keeping a private copy of the map in the view,
but that's a waste of memory for as rare of a problem as this. */
if (changed)
return 0;
}
static int
unsigned int *expunge_count_r)
{
struct mail_transaction_header thdr;
/* we don't update the map in the same order as it's typically done.
map->rec_map may already have some messages appended that we don't
want. get an atomic map to make sure these get removed. */
&ctx->lost_new_ext_idx))
/* handle expunges and sync flags */
/* message found - check if flags have changed */
return -1;
/* message expunged */
seqi++;
} else {
/* new message appeared out of nowhere */
"%s view is inconsistent: "
"uid=%u inserted in the middle of mailbox",
return -1;
}
}
/* if there are old messages left, they're all expunged */
}
/* if there are new messages left, they're all new messages */
return -1;
&ctx->lost_new_kw);
return -1;
}
/* we have no idea how far we've synced - make sure these aren't used */
} else {
}
return 0;
}
{
const char *reason;
bool reset;
int ret;
/* replace the view's map */
/* update log positions */
if (ret < 0)
return -1;
if (ret == 0) {
return 0;
}
return 0;
}
struct mail_index_view_sync_ctx *
{
struct mail_index_view_sync_ctx *ctx;
struct mail_index_map *tmp_map;
unsigned int expunge_count = 0;
int ret;
/* Syncing the view invalidates all previous looked up records.
Unreference the mappings this view keeps because of them. */
if (sync_expunges)
if ((flags & MAIL_INDEX_VIEW_SYNC_FLAG_FIX_INCONSISTENT) != 0) {
/* just get this view synced - don't return anything */
if (mail_index_view_sync_init_fix(ctx) < 0)
return ctx;
}
if (mail_index_view_is_inconsistent(view)) {
return ctx;
}
if (ret < 0) {
return ctx;
}
if (ret == 0) {
if (!sync_expunges)
if (ret < 0) {
"%s view syncing failed to apply changes",
return ctx;
}
have_expunges = expunge_count > 0;
} else if (sync_expunges) {
/* get list of all expunges first */
return ctx;
}
have_expunges = expunge_count > 0;
} else {
}
"%s reset, view is now inconsistent",
}
if (!have_expunges) {
/* no expunges, we can just replace the map */
"Index %s lost messages without expunging "
ctx->finish_min_msg_count = 0;
}
} else {
/* expunges seen. create a private map which we update.
if we're syncing expunges the map will finally be replaced
with the head map to remove the expunged messages. */
}
if (sync_expunges) {
}
}
#ifdef DEBUG
#endif
return ctx;
}
static bool
{
const struct mail_index_view_log_sync_area *sync;
return FALSE;
return TRUE;
}
return FALSE;
}
static bool
const struct mail_transaction_header *hdr)
{
MAIL_TRANSACTION_EXPUNGE_GUID)) != 0 &&
if (!ctx->skipped_expunges) {
}
return FALSE;
}
/* already synced */
return FALSE;
}
}
view->log_file_head_offset)) {
/* already synced */
return FALSE;
}
return TRUE;
}
static int
{
const struct mail_transaction_header *hdr;
int ret;
bool synced_to_map;
do {
/* Get the next transaction from log. */
if (ret <= 0) {
if (ret < 0)
return -1;
return 0;
}
/* skip records we've already synced */
/* If we started from a map that we didn't create ourself,
some of the transactions may already be synced. at the end
of this view sync we'll update file_seq=0 so that this check
always becomes FALSE for subsequent syncs. */
/* Apply transaction to view's mapping if needed (meaning we
didn't just re-map the view to head mapping). */
MAIL_TRANSACTION_EXPUNGE_GUID)) == 0) {
}
if (ret < 0)
return -1;
}
return 1;
}
static bool
struct mail_index_view_sync_rec *rec)
{
case MAIL_TRANSACTION_FLAG_UPDATE: {
const struct mail_transaction_flag_update *update =
/* data contains mail_transaction_flag_update[] */
for (;;) {
break;
/* skip internal flag changes */
return FALSE;
}
else
break;
}
case MAIL_TRANSACTION_KEYWORD_UPDATE: {
/* data contains mail_transaction_keyword_update header,
the keyword name and an array of { uint32_t uid1, uid2; } */
if (ctx->data_offset == 0) {
/* skip over the header and name */
}
break;
}
case MAIL_TRANSACTION_KEYWORD_RESET: {
const struct mail_transaction_keyword_reset *reset =
/* data contains mail_transaction_keyword_reset[] */
break;
}
default:
return FALSE;
}
return TRUE;
}
static bool
struct mail_index_view_sync_rec *sync_rec)
{
unsigned int count;
return FALSE;
}
ctx->lost_flag_idx++;
return TRUE;
}
struct mail_index_view_sync_rec *sync_rec)
{
int ret;
if (ctx->log_was_lost)
do {
if (ret <= 0) {
if (ret < 0)
return FALSE;
}
ctx->data_offset = 0;
}
return TRUE;
}
{
}
static void
{
const struct mail_index_view_log_sync_area *syncs;
unsigned int i, count;
return;
/* Clean up to view's tail */
for (i = 0; i < count; i++) {
if ((syncs[i].log_file_offset +
break;
}
if (i > 0)
}
bool *delayed_expunges_r)
{
/* we didn't sync everything */
ret = -1;
}
} else if (ctx->sync_map_update) {
/* log offsets have no meaning in views. make sure they're not
tried to be used wrong by setting them to zero. */
}
if (!ctx->skipped_expunges) {
}
#ifdef DEBUG
#endif
/* set log view to empty range so unneeded memory gets freed */
return ret;
}
unsigned int length)
{
struct mail_index_view_log_sync_area *area;
}