maildir-sync.c revision 8e5fedd9ada47735be8ac0f8af2a66e8528bd776
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen/* Copyright (C) 2004 Timo Sirainen */
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen Here's a description of how we handle Maildir synchronization and
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen it's problems:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen We want to be as efficient as we can. The most efficient way to
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen check if changes have occurred is to stat() the new/ and cur/
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen directories and uidlist file - if their mtimes haven't changed,
daf029d2a627daa39d05507140f385162828172eTimo Sirainen there's no changes and we don't need to do anything.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen Problem 1: Multiple changes can happen within a single second -
9b7eeffb5752b500ac62ba1fd01c4a8c4ada14e9Timo Sirainen nothing guarantees that once we synced it, someone else didn't just
9b7eeffb5752b500ac62ba1fd01c4a8c4ada14e9Timo Sirainen then make a modification. Such modifications wouldn't get noticed
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen until a new modification occurred later.
007d354a674fb3ddf49db160cf050cf61270a1a0Timo Sirainen Problem 2: Syncing cur/ directory is much more costly than syncing
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen new/. Moving mails from new/ to cur/ will always change mtime of
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen cur/ causing us to sync it as well.
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen Problem 3: We may not be able to move mail from new/ to cur/
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen because we're out of quota, or simply because we're accessing a
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen read-only mailbox.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen MAILDIR_SYNC_SECS
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen -----------------
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen Several checks below use MAILDIR_SYNC_SECS, which should be maximum
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen clock drift between all computers accessing the maildir (eg. via
f923659c0e5298263d80622c99f4dc4132b4675bTimo Sirainen NFS), rounded up to next second. Our default is 1 second, since
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen everyone should be using NTP.
f923659c0e5298263d80622c99f4dc4132b4675bTimo Sirainen Note that setting it to 0 works only if there's only one computer
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen accessing the maildir. It's practically impossible to make two
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen clocks _exactly_ synchronized.
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen It might be possible to only use file server's clock by looking at
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen the atime field, but I don't know how well that would actually work.
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen cur directory
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen -------------
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen We have dirty_cur_time variable which is set to cur/ directory's
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen mtime when it's >= time() - MAILDIR_SYNC_SECS and we _think_ we have
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen synchronized the directory.
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen When dirty_cur_time is non-zero, we don't synchronize the cur/
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen directory until
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen a) cur/'s mtime changes
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen b) opening a mail fails with ENOENT
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen c) time() > dirty_cur_time + MAILDIR_SYNC_SECS
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen This allows us to modify the maildir multiple times without having
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen to sync it at every change. The sync will eventually be done to
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen make sure we didn't miss any external changes.
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen The dirty_cur_time is set when:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen - we change message flags
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen - we expunge messages
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "buffer.h"
#include "hash.h"
#include "str.h"
#include "nfs-workarounds.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"
#include "maildir-filename.h"
#include "maildir-sync.h"
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
struct maildir_sync_context {
bool partial;
/* we got here from maildir-save.c. it has no
static struct maildir_sync_context *
return ctx;
int ret = 0;
fname2);
t_push();
t_pop();
t_pop();
t_pop();
return ret;
const char *dir;
unsigned int i = 0, move_count = 0;
t_push();
errno = 0;
if (ret == 0) {
if (new_dir)
if (ret < 0)
flags = 0;
if (move_new) {
move_count++;
move_count++;
} else if (new_dir) {
if ((i % MAILDIR_SLOW_CHECK_COUNT) == 0)
if (ret <= 0) {
if (ret < 0)
if (errno != 0) {
t_pop();
bool sync_last_commit)
int ret;
if (sync_last_commit) {
} else if (!forced) {
if (ret <= 0) {
return ret;
unsigned int count = 0;
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();