maildir-sync.c revision 24ddec4ee3571be32f615138b56cf0ae6f922af0
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (C) 2004 Timo Sirainen */
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen Here's a description of how we handle Maildir synchronization and
bf72c930996df0691932fb1143f360d260f27a06Timo Sirainen it's problems:
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen We want to be as efficient as we can. The most efficient way to
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen check if changes have occurred is to stat() the new/ and cur/
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen directories and uidlist file - if their mtimes haven't changed,
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen there's no changes and we don't need to do anything.
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen Problem 1: Multiple changes can happen within a single second -
12cf3d0e03fc70fb0c8b91bc8fd83b4e14d7cdefTimo Sirainen nothing guarantees that once we synced it, someone else didn't just
8222ce68120b51353a3b31d3073b5f845d0e9f53Timo Sirainen then make a modification. Such modifications wouldn't get noticed
bf72c930996df0691932fb1143f360d260f27a06Timo Sirainen until a new modification occurred later.
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen Problem 2: Syncing cur/ directory is much more costly than syncing
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen new/. Moving mails from new/ to cur/ will always change mtime of
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen cur/ causing us to sync it as well.
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen Problem 3: We may not be able to move mail from new/ to cur/
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen because we're out of quota, or simply because we're accessing a
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen read-only mailbox.
02ccba3d3be96444abd15b5254864c9151bbeb30Timo Sirainen MAILDIR_SYNC_SECS
bf72c930996df0691932fb1143f360d260f27a06Timo Sirainen -----------------
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen Several checks below use MAILDIR_SYNC_SECS, which should be maximum
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen clock drift between all computers accessing the maildir (eg. via
02ccba3d3be96444abd15b5254864c9151bbeb30Timo Sirainen NFS), rounded up to next second. Our default is 1 second, since
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen everyone should be using NTP.
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen Note that setting it to 0 works only if there's only one computer
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen accessing the maildir. It's practically impossible to make two
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen clocks _exactly_ synchronized.
64e244defe74f513ce94f33d000a048ddbe2ea23Timo Sirainen It might be possible to only use file server's clock by looking at
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen the atime field, but I don't know how well that would actually work.
ad004e44be109684521494b5af2ad1da39b8bb27Timo Sirainen cur directory
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen -------------
9439bed2f07d6475febd8a247cd2f0990fb32a13Timo Sirainen We have dirty_cur_time variable which is set to cur/ directory's
c49a19168dab6fda80aee16ad799a8a56d3bc18fTimo Sirainen mtime when it's >= time() - MAILDIR_SYNC_SECS and we _think_ we have
2a90d8a14b0e7cc1508814bc87d3dfa598ef46a8Timo Sirainen synchronized the directory.
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen When dirty_cur_time is non-zero, we don't synchronize the cur/
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen directory until
caea325346da6fb1cf503b35a619467a997acbfaTimo Sirainen a) cur/'s mtime changes
caea325346da6fb1cf503b35a619467a997acbfaTimo Sirainen b) opening a mail fails with ENOENT
c0a708fa3f7b8f4fbca32052da5faf7a0125189dTimo Sirainen c) time() > dirty_cur_time + MAILDIR_SYNC_SECS
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "buffer.h"
#include "hash.h"
#include "str.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"
#include "maildir-keywords.h"
#include <stdio.h>
#include <stddef.h>
#include <unistd.h>
#include <dirent.h>
struct maildir_sync_context {
bool partial;
struct maildir_index_sync_context {
int dirty_state;
struct maildir_keywords_sync_ctx *
const char *info;
*flags_r = 0;
switch (*info) {
int idx;
if (idx < 0) {
const unsigned int *indexes;
unsigned int i, count;
char chr;
for (i = 0; i < count; i++) {
int nextflag;
oldflags++;
oldflags++;
void *context)
const char *newpath;
unsigned int i, count;
for (i = 0; i < count; i++) {
i_unreached();
unsigned int i, count;
for (i = 0; i < count; i++) {
i_unreached();
if (expunged) {
} else if (flag_changed) {
for (i = count; i > 0; i--) {
&count);
if (count == 0) {
int ret;
if (ret <= 0)
return ret;
} while (ret > 0);
return ret;
static struct maildir_sync_context *
return ctx;
const char *old_fname)
int ret = 0;
t_push();
t_pop();
return ret;
const char *dir;
unsigned int moves = 0;
bool move_new;
t_push();
if (ret == 0) {
if (new_dir)
if (ret < 0)
flags = 0;
if (move_new) {
moves++;
moves++;
} else if (new_dir) {
if (ret <= 0) {
if (ret < 0)
t_pop();
cur_mtime : 0;
struct maildir_index_sync_context *
return NULL;
return sync_ctx;
bool partial)
const char *filename;
int ret = 0;
seq = 0;
unsigned int, MAILDIR_MAX_KEYWORDS);
unsigned int, MAILDIR_MAX_KEYWORDS);
seq++;
if ((uflags &
MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) {
if ((uflags &
MAILDIR_UIDLIST_REC_FLAG_RACING) != 0) {
filename);
seq--;
flags);
goto __again;
if ((uflags &
MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) {
seq--;
seq--;
flags);
if (!partial) {
if (uid_validity == 0) {
} else if (uid_validity == 0) {
if (ret < 0) {
else if (seq != 0) {
if (ret == 0) {
bool sync_last_commit)
int ret;
if (sync_last_commit) {
} else if (!forced) {
if (ret <= 0) {
return ret;
if (ret < 0)
if (cur_changed) {
if (ret < 0) {
if (ret == 0)
int ret;
int ret;
struct mailbox_sync_context *
int ret = 0;
ioloop_time) {
if (ret == 0) {
int ret;
t_push();
t_pop();