const ARRAY_TYPE(keywords) *keywords;
ARRAY_DEFINE(idx_to_chr, char);
unsigned int chridx_to_idx[MAILDIR_MAX_KEYWORDS];
+ bool readonly;
};
struct maildir_keywords *maildir_keywords_init(struct maildir_mailbox *mbox)
return ctx;
}
+struct maildir_keywords_sync_ctx *
+maildir_keywords_sync_init_readonly(struct maildir_keywords *mk,
+ struct mail_index *index)
+{
+ struct maildir_keywords_sync_ctx *ctx;
+
+ ctx = maildir_keywords_sync_init(mk, index);
+ ctx->readonly = TRUE;
+ return ctx;
+}
+
void maildir_keywords_sync_deinit(struct maildir_keywords_sync_ctx **_ctx)
{
struct maildir_keywords_sync_ctx *ctx = *_ctx;
const char *const *name_p;
char *chr_p;
unsigned int chridx;
+ int ret;
chr_p = array_idx_modifiable(&ctx->idx_to_chr, idx);
if (*chr_p != '\0')
return *chr_p;
name_p = array_idx(ctx->keywords, idx);
- if (maildir_keywords_lookup_or_create(ctx->mk, *name_p, &chridx) <= 0)
+ ret = !ctx->readonly ?
+ maildir_keywords_lookup_or_create(ctx->mk, *name_p, &chridx) :
+ maildir_keywords_lookup(ctx->mk, *name_p, &chridx);
+ if (ret <= 0)
return '\0';
*chr_p = chridx + MAILDIR_KEYWORD_FIRST;
struct maildir_keywords_sync_ctx *
maildir_keywords_sync_init(struct maildir_keywords *mk,
struct mail_index *index);
+/* Don't try to add any nonexisting keywords */
+struct maildir_keywords_sync_ctx *
+maildir_keywords_sync_init_readonly(struct maildir_keywords *mk,
+ struct mail_index *index);
void maildir_keywords_sync_deinit(struct maildir_keywords_sync_ctx **ctx);
/* Returns keyword index. */
index = index_storage_alloc(&storage->storage, name, flags,
MAILDIR_INDEX_PREFIX);
mbox->ibox.index = index;
+ mbox->very_dirty_syncs = getenv("MAILDIR_VERY_DIRTY_SYNCS") != NULL;
/* for shared mailboxes get the create mode from the
permissions of dovecot-shared file. */
timeout_remove(&mbox->keep_lock_to);
}
+ if (mbox->flags_view != NULL)
+ mail_index_view_close(&mbox->flags_view);
if (mbox->keywords != NULL)
maildir_keywords_deinit(&mbox->keywords);
maildir_uidlist_deinit(&mbox->uidlist);
struct maildir_mailbox {
struct index_mailbox ibox;
struct maildir_storage *storage;
+ struct mail_index_view *flags_view;
const char *path;
struct timeout *keep_lock_to;
uint32_t maildir_ext_id;
unsigned int syncing_commit:1;
+ unsigned int very_dirty_syncs:1;
};
struct maildir_transaction_context {
uint32_t first_uid;
unsigned int changes = 0;
int ret = 0;
+ time_t time_before_sync;
+ struct stat st;
bool expunged, full_rescan = FALSE;
i_assert(!mbox->syncing_commit);
}
hdr_next_uid = hdr->next_uid;
+ time_before_sync = time(NULL);
mbox->syncing_commit = TRUE;
seq = prev_uid = 0; first_recent_uid = I_MAX(hdr->first_recent_uid, 1);
t_array_init(&ctx->keywords, MAILDIR_MAX_KEYWORDS);
if (mbox->ibox.box.v.sync_notify != NULL)
mbox->ibox.box.v.sync_notify(&mbox->ibox.box, 0, 0);
- if (ctx->changed)
- mbox->maildir_hdr.cur_mtime = time(NULL);
+ if (stat(t_strconcat(mbox->path, "/cur", NULL), &st) == 0) {
+ mbox->maildir_hdr.new_check_time =
+ I_MAX(st.st_mtime, time_before_sync);
+ mbox->maildir_hdr.cur_mtime = st.st_mtime;
+ mbox->maildir_hdr.cur_mtime_nsecs = ST_MTIME_NSEC(st);
+ }
if (uid_validity == 0) {
uid_validity = hdr->uid_validity != 0 ? hdr->uid_validity :
}
if (dir_changed) {
- if (new_dir)
- ctx->mbox->maildir_hdr.new_mtime = now;
- else
- ctx->mbox->maildir_hdr.cur_mtime = now;
+ /* save the exact new times. the new mtimes should be >=
+ "now", but just in case something weird happens and mtime
+ doesn't update, use "now". */
+ if (stat(ctx->new_dir, &st) == 0) {
+ ctx->mbox->maildir_hdr.new_check_time =
+ I_MAX(st.st_mtime, now);
+ ctx->mbox->maildir_hdr.new_mtime = st.st_mtime;
+ ctx->mbox->maildir_hdr.new_mtime_nsecs =
+ ST_MTIME_NSEC(st);
+ }
+ if (stat(ctx->cur_dir, &st) == 0) {
+ ctx->mbox->maildir_hdr.new_check_time =
+ I_MAX(st.st_mtime, now);
+ ctx->mbox->maildir_hdr.cur_mtime = st.st_mtime;
+ ctx->mbox->maildir_hdr.cur_mtime_nsecs =
+ ST_MTIME_NSEC(st);
+ }
}
return ret < 0 ? -1 :
/* try to avoid stat()ing by first checking delayed changes */
if (DIR_DELAYED_REFRESH(hdr, new) ||
- DIR_DELAYED_REFRESH(hdr, cur)) {
+ (DIR_DELAYED_REFRESH(hdr, cur) && !mbox->very_dirty_syncs)) {
/* refresh index and try again */
if (maildir_sync_header_refresh(mbox) < 0)
return -1;
if (DIR_DELAYED_REFRESH(hdr, new))
*new_changed_r = TRUE;
- if (DIR_DELAYED_REFRESH(hdr, cur))
+ if (DIR_DELAYED_REFRESH(hdr, cur) && !mbox->very_dirty_syncs)
*cur_changed_r = TRUE;
if (*new_changed_r && *cur_changed_r)
return 0;
}
}
+ if (mbox->very_dirty_syncs) {
+ struct mail_index_view_sync_ctx *sync_ctx;
+ bool b;
+
+ if (mbox->flags_view == NULL) {
+ mbox->flags_view =
+ mail_index_view_open(mbox->ibox.index);
+ }
+ sync_ctx = mail_index_view_sync_begin(mbox->flags_view,
+ MAIL_INDEX_VIEW_SYNC_FLAG_FIX_INCONSISTENT);
+ if (mail_index_view_sync_commit(&sync_ctx, &b) < 0) {
+ mail_storage_set_index_error(&mbox->ibox);
+ ret = -1;
+ }
+ /* make sure the map stays in private memory */
+ if (mbox->flags_view->map->refcount > 1) {
+ struct mail_index_map *map;
+
+ map = mail_index_map_clone(mbox->flags_view->map);
+ mail_index_unmap(&mbox->flags_view->map);
+ mbox->flags_view->map = map;
+ }
+ mail_index_record_map_move_to_private(mbox->flags_view->map);
+ mail_index_map_move_to_memory(mbox->flags_view->map);
+ maildir_uidlist_set_all_nonsynced(mbox->uidlist);
+ }
return index_mailbox_sync_init(box, flags, ret < 0);
}
return 1;
}
+void maildir_uidlist_set_all_nonsynced(struct maildir_uidlist *uidlist)
+{
+ maildir_uidlist_mark_all(uidlist, TRUE);
+}
+
int maildir_uidlist_sync_init(struct maildir_uidlist *uidlist,
enum maildir_uidlist_sync_flags sync_flags,
struct maildir_uidlist_sync_ctx **sync_ctx_r)
maildir_uidlist_set_ext() */
int maildir_uidlist_update(struct maildir_uidlist *uidlist);
+void maildir_uidlist_set_all_nonsynced(struct maildir_uidlist *uidlist);
/* Sync uidlist with what's actually on maildir. Returns same as
maildir_uidlist_lock(). */
int maildir_uidlist_sync_init(struct maildir_uidlist *uidlist,
/* Copyright (c) 2004-2009 Dovecot authors, see the included COPYING file */
#include "lib.h"
+#include "array.h"
#include "ioloop.h"
#include "str.h"
#include "mkdir-parents.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"
+#include "maildir-keywords.h"
+#include "maildir-filename.h"
#include "maildir-sync.h"
#include <unistd.h>
#define MAILDIR_RESYNC_RETRY_COUNT 10
+static const char *
+maildir_filename_guess(struct maildir_mailbox *mbox, uint32_t uid,
+ const char *fname, bool *have_flags_r)
+
+{
+ struct mail_index_view *view = mbox->flags_view;
+ struct maildir_keywords_sync_ctx *kw_ctx;
+ enum mail_flags flags;
+ ARRAY_TYPE(keyword_indexes) keywords;
+ uint32_t seq;
+
+ if (view == NULL || !mail_index_lookup_seq(view, uid, &seq)) {
+ *have_flags_r = FALSE;
+ return fname;
+ }
+
+ t_array_init(&keywords, 32);
+ mail_index_lookup_view_flags(view, seq, &flags, &keywords);
+ if (array_count(&keywords) == 0) {
+ *have_flags_r = (flags & MAIL_FLAGS_NONRECENT) != 0;
+ fname = maildir_filename_set_flags(NULL, fname, flags, NULL);
+ } else {
+ *have_flags_r = TRUE;
+ kw_ctx = maildir_keywords_sync_init_readonly(mbox->keywords,
+ mbox->ibox.index);
+ fname = maildir_filename_set_flags(kw_ctx, fname,
+ flags, &keywords);
+ maildir_keywords_sync_deinit(&kw_ctx);
+ }
+ return fname;
+}
+
static int maildir_file_do_try(struct maildir_mailbox *mbox, uint32_t uid,
maildir_file_do_func *callback, void *context)
{
- const char *fname;
- enum maildir_uidlist_rec_flag flags;
+ const char *path, *fname;
+ enum maildir_uidlist_rec_flag flags;
+ bool have_flags;
int ret;
fname = maildir_uidlist_lookup(mbox->uidlist, uid, &flags);
if (fname == NULL)
return -2; /* expunged */
+ if ((flags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) {
+ /* let's see if we can guess the filename based on index */
+ fname = maildir_filename_guess(mbox, uid, fname, &have_flags);
+ if (have_flags) {
+ /* don't even bother looking into new/ dir */
+ flags &= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
+ }
+ }
+
if ((flags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0) {
/* probably in new/ dir */
- T_BEGIN {
- const char *path;
-
- path = t_strconcat(mbox->path, "/new/", fname, NULL);
- ret = callback(mbox, path, context);
- } T_END;
+ path = t_strconcat(mbox->path, "/new/", fname, NULL);
+ ret = callback(mbox, path, context);
if (ret != 0)
return ret;
}
- T_BEGIN {
- const char *path;
-
- path = t_strconcat(mbox->path, "/cur/", fname, NULL);
- ret = callback(mbox, path, context);
- } T_END;
+ path = t_strconcat(mbox->path, "/cur/", fname, NULL);
+ ret = callback(mbox, path, context);
return ret;
}
{
int i, ret;
- ret = maildir_file_do_try(mbox, uid, callback, context);
+ T_BEGIN {
+ ret = maildir_file_do_try(mbox, uid, callback, context);
+ } T_END;
for (i = 0; i < MAILDIR_RESYNC_RETRY_COUNT && ret == 0; i++) {
/* file is either renamed or deleted. sync the maildir and
see which one. if file appears to be renamed constantly,
if (maildir_storage_sync_force(mbox, uid) < 0)
return -1;
- ret = maildir_file_do_try(mbox, uid, callback, context);
+ T_BEGIN {
+ ret = maildir_file_do_try(mbox, uid, callback, context);
+ } T_END;
}
- if (i == MAILDIR_RESYNC_RETRY_COUNT)
+ if (i == MAILDIR_RESYNC_RETRY_COUNT) T_BEGIN {
ret = maildir_file_do_try(mbox, uid, do_racecheck, context);
+ } T_END;
return ret == -2 ? 0 : ret;
}
env_put("MAILDIR_COPY_WITH_HARDLINKS=1");
if (set->maildir_copy_preserve_filename)
env_put("MAILDIR_COPY_PRESERVE_FILENAME=1");
+ if (set->maildir_very_dirty_syncs)
+ env_put("MAILDIR_VERY_DIRTY_SYNCS=1");
if (set->mail_debug)
env_put("DEBUG=1");
if (set->mail_full_filesystem_access)
DEF_BOOL(maildir_stat_dirs),
DEF_BOOL(maildir_copy_with_hardlinks),
DEF_BOOL(maildir_copy_preserve_filename),
+ DEF_BOOL(maildir_very_dirty_syncs),
DEF_STR(mbox_read_locks),
DEF_STR(mbox_write_locks),
DEF_INT(mbox_lock_timeout),
MEMBER(maildir_stat_dirs) FALSE,
MEMBER(maildir_copy_with_hardlinks) TRUE,
MEMBER(maildir_copy_preserve_filename) FALSE,
+ MEMBER(maildir_very_dirty_syncs) FALSE,
MEMBER(mbox_read_locks) "fcntl",
MEMBER(mbox_write_locks) "dotlock fcntl",
MEMBER(mbox_lock_timeout) 300,
bool maildir_stat_dirs;
bool maildir_copy_with_hardlinks;
bool maildir_copy_preserve_filename;
+ bool maildir_very_dirty_syncs;
const char *mbox_read_locks;
const char *mbox_write_locks;
unsigned int mbox_lock_timeout;