maildir-sync-index.c revision 77f8c49beac69a8f5d5d41239528474ee1b877ea
/* Copyright (c) 2007-2017 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "maildir-storage.h"
#include "index-sync-changes.h"
#include "maildir-uidlist.h"
#include "maildir-keywords.h"
#include "maildir-filename-flags.h"
#include "maildir-sync.h"
#include "mailbox-recent-flags.h"
#include <stdio.h>
#include <unistd.h>
struct maildir_index_sync_context {
struct maildir_mailbox *mbox;
struct maildir_sync_context *maildir_sync_ctx;
struct mail_index_view *view;
struct mail_index_sync_ctx *sync_ctx;
struct mail_index_transaction *trans;
struct maildir_uidlist_sync_ctx *uidlist_sync_ctx;
struct index_sync_changes_context *sync_changes;
enum mail_flags flags;
bool update_maildir_hdr_cur;
};
struct maildir_keywords_sync_ctx *
{
return ctx->keywords_sync_ctx;
}
unsigned int count)
{
}
static bool
{
const char *guid;
if (guid_128_is_empty(expunged_guid_128)) {
/* no GUID associated with expunge */
return TRUE;
}
T_BEGIN {
} T_END;
return TRUE;
"Mailbox %s: Expunged GUID mismatch for UID %u: %s vs %s",
return FALSE;
}
struct maildir_index_sync_context *ctx)
{
ctx->expunge_count++;
}
return 1;
}
return 0;
if (UNLINK_EISDIR(errno))
"unlink(%s) failed: %m", path);
return -1;
}
struct maildir_index_sync_context *ctx)
{
ctx->flag_change_count++;
fname++;
/* get the current flags and keywords */
/* apply changes */
/* and try renaming with the new name */
/* just make sure that the file still exists. avoid rename()
here because it's slow on HFS. */
return 0;
"stat(%s) failed: %m", path);
return -1;
}
} else {
return 0;
"rename(%s, %s) failed: %m",
}
return -1;
}
}
}
return 1;
}
{
int ret;
if ((uflags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) {
/* partial syncing */
return 0;
}
/* most likely a race condition: we read the maildir, then someone else
expunged messages and committed changes to index. so, this message
shouldn't actually exist. */
if ((uflags & MAILDIR_UIDLIST_REC_FLAG_RACING) == 0) {
/* mark it racy and check in next sync */
return 0;
}
&ctx->uidlist_sync_ctx);
if (ret <= 0)
return -1;
}
/* give the new UID to it immediately */
i_warning("Maildir %s: Expunged message reappeared, giving a new UID "
" (Your MDA is saving MH files into Maildir?)");
return 0;
}
struct maildir_sync_context *maildir_sync_ctx,
struct maildir_index_sync_context **ctx_r)
{
struct maildir_index_sync_context *ctx;
struct mail_index_sync_ctx *sync_ctx;
struct mail_index_view *view;
struct mail_index_transaction *trans;
/* don't drop recent messages if we're saving messages */
if (maildir_sync_ctx == NULL)
&trans, sync_flags) < 0)
return -1;
ctx->sync_changes =
return 0;
}
static bool
const struct maildir_index_header *new_hdr)
{
return TRUE;
}
static void
{
const char *cur_path;
const void *data;
}
0, &mbox->maildir_hdr,
sizeof(mbox->maildir_hdr));
}
}
bool success)
{
unsigned int time_diff;
if (time_diff >= MAILDIR_SYNC_TIME_WARN_SECS) {
i_warning("Maildir %s: Synchronization took %u seconds "
"(%u new msgs, %u flag change attempts, "
"%u expunge attempts)",
ctx->expunge_count);
}
if (ret < 0)
else {
/* Set syncing_commit=TRUE so that if any sync callbacks try
to access mails which got lost (eg. expunge callback trying
to open the file which was just unlinked) we don't try to
start a second index sync and crash. */
ret = -1;
}
}
return ret;
}
{
}
{
}
{
return -1;
return 1;
else
return 0;
}
static void
{
struct mail_keywords *kw;
const unsigned int *old_indexes, *new_indexes;
bool have_indexonly_keywords;
int diff;
/* no changes - we should get here usually */
return;
}
/* sort the keywords */
/* drop keywords that are in index-only. we don't want to touch them. */
for (i = old_count; i > 0; i--) {
}
}
if (!have_indexonly_keywords) {
/* no index-only keywords found, so something changed.
just replace them all. */
return;
}
/* check again if non-index-only keywords changed */
return;
/* we can't reset all the keywords or we'd drop indexonly keywords too.
so first remove the unwanted keywords and then add back the wanted
ones. we can get these lists easily by removing common elements
from old and new keywords. */
if (diff == 0) {
} else if (diff < 0) {
i++;
} else {
j++;
}
}
&ctx->idx_keywords);
}
}
}
bool partial)
{
struct mail_index_view *view2;
struct maildir_uidlist_iter_ctx *iter;
const struct mail_index_header *hdr;
struct mail_index_header empty_hdr;
const struct mail_index_record *rec;
const char *filename;
unsigned int changes = 0;
int ret = 0;
enum mail_flags private_flags_mask;
first_uid = 1;
/* uidvalidity changed and index isn't being synced for the
first time, reset the index so we can add all messages as
new */
i_warning("Maildir %s: UIDVALIDITY changed (%u -> %u)",
}
/* the private flags are kept only in indexes. don't use them
at all even for newly seen mails */
seq++;
if (uid < hdr_next_uid) {
uid) < 0)
ret = -1;
seq--;
continue;
}
/* Trust uidlist recent flags only for newly added
they're stored to cur/ and uidlist treats them
as non-recent. */
if ((uflags & MAILDIR_UIDLIST_REC_FLAG_RECENT) == 0) {
if (uid >= first_recent_uid)
}
struct mail_keywords *kw;
MODIFY_REPLACE, kw);
}
continue;
}
/* already expunged (no point in showing guid in the
expunge record anymore) */
goto again;
}
ret = -1;
seq--;
continue;
}
if (expunged) {
continue;
maildir_expunge, ctx) >= 0) {
/* successful expunge */
}
if ((++changes % MAILDIR_SLOW_MOVE_COUNT) == 0)
continue;
}
/* the private flags are stored only in indexes, keep them */
/* apply flag changes to maildir */
maildir_sync_flags, ctx) < 0)
if ((++changes % MAILDIR_SLOW_MOVE_COUNT) == 0)
}
if ((uflags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) {
/* partial syncing */
if ((uflags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0) {
/* we last saw this mail in new/, but it's
not there anymore. possibly expunged,
make sure. */
full_rescan = TRUE;
}
continue;
}
/* we haven't been able to update maildir with this
record's flag changes. don't sync them. */
continue;
}
}
}
if (!partial) {
/* expunge the rest */
}
/* add \Recent flags. use updated view so it contains newly
appended messages. */
/* UIDVALIDITY changed, skip over the old messages */
}
}
TRUE) < 0)
ret = -1;
}
/* check cur/ mtime later. if we came here from saving messages they
could still be moved to cur/ directory. */
if (uid_validity == 0) {
}
}
if (hdr_next_uid < next_uid) {
}
}
}
static unsigned int
struct mail_index_view *view)
{
"maildir", 0,
sizeof(struct maildir_list_index_record),
sizeof(uint32_t));
}
return mbox->maildir_list_index_ext_id;
}
struct mail_index_view *list_view,
{
const struct maildir_list_index_record *rec;
const void *data;
bool expunged;
int ret;
return ret;
return 0;
}
/* doesn't exist, not synced or dirty-synced */
return 1;
}
&root_dir);
if (ret < 0)
return ret;
/* check if new/ changed */
"stat(%s) failed: %m", new_dir);
return -1;
}
return 1;
/* check if cur/ changed */
"stat(%s) failed: %m", cur_dir);
return -1;
}
return 1;
return 0;
}
struct mail_index_transaction *trans,
{
struct mail_index_view *list_view;
const struct maildir_list_index_record *old_rec;
struct maildir_list_index_record new_rec;
const void *data;
bool expunged;
return;
}
/* get the current record */
if (expunged)
return;
/* dirty, we need a refresh next time */
} else {
}
}