bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/*
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen Here's a description of how we handle Maildir synchronization and
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen it's problems:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen We want to be as efficient as we can. The most efficient way to
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen check if changes have occurred is to stat() the new/ and cur/
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen directories and uidlist file - if their mtimes haven't changed,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen there's no changes and we don't need to do anything.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen Problem 1: Multiple changes can happen within a single second -
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen nothing guarantees that once we synced it, someone else didn't just
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen then make a modification. Such modifications wouldn't get noticed
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen until a new modification occurred later.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen Problem 2: Syncing cur/ directory is much more costly than syncing
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen new/. Moving mails from new/ to cur/ will always change mtime of
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen cur/ causing us to sync it as well.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen Problem 3: We may not be able to move mail from new/ to cur/
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen because we're out of quota, or simply because we're accessing a
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen read-only mailbox.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen MAILDIR_SYNC_SECS
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen -----------------
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen Several checks below use MAILDIR_SYNC_SECS, which should be maximum
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen clock drift between all computers accessing the maildir (eg. via
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen NFS), rounded up to next second. Our default is 1 second, since
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen everyone should be using NTP.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen Note that setting it to 0 works only if there's only one computer
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen accessing the maildir. It's practically impossible to make two
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen clocks _exactly_ synchronized.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen It might be possible to only use file server's clock by looking at
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen the atime field, but I don't know how well that would actually work.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen cur directory
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen -------------
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen We have dirty_cur_time variable which is set to cur/ directory's
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen mtime when it's >= time() - MAILDIR_SYNC_SECS and we _think_ we have
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen synchronized the directory.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen When dirty_cur_time is non-zero, we don't synchronize the cur/
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen directory until
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen a) cur/'s mtime changes
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen b) opening a mail fails with ENOENT
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen c) time() > dirty_cur_time + MAILDIR_SYNC_SECS
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen This allows us to modify the maildir multiple times without having
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen to sync it at every change. The sync will eventually be done to
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen make sure we didn't miss any external changes.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen The dirty_cur_time is set when:
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen - we change message flags
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen - we expunge messages
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen - we move mail from new/ to cur/
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen - we sync cur/ directory and it's mtime is >= time() - MAILDIR_SYNC_SECS
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen It's unset when we do the final syncing, ie. when mtime is
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen older than time() - MAILDIR_SYNC_SECS.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen new directory
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen -------------
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen If new/'s mtime is >= time() - MAILDIR_SYNC_SECS, always synchronize
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen it. dirty_cur_time-like feature might save us a few syncs, but
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen that might break a client which saves a mail in one connection and
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen tries to fetch it in another one. new/ directory is almost always
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen empty, so syncing it should be very fast anyway. Actually this can
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen still happen if we sync only new/ dir while another client is also
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen moving mails from it to cur/ - it takes us a while to see them.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen That's pretty unlikely to happen however, and only way to fix it
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen would be to always synchronize cur/ after new/.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen Normally we move all mails from new/ to cur/ whenever we sync it. If
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen it's not possible for some reason, we mark the mail with "probably
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen exists in new/ directory" flag.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen If rename() still fails because of ENOSPC or EDQUOT, we still save
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen the flag changes in index with dirty-flag on. When moving the mail
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen to cur/ directory, or when we notice it's already moved there, we
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen apply the flag changes to the filename, rename it and remove the
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen dirty flag. If there's dirty flags, this should be tried every time
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen after expunge or when closing the mailbox.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen uidlist
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen -------
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen This file contains UID <-> filename mappings. It's updated only when
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen new mail arrives, so it may contain filenames that have already been
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen deleted. Updating is done by getting uidlist.lock file, writing the
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen whole uidlist into it and rename()ing it over the old uidlist. This
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen means there's no need to lock the file for reading.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen Whenever uidlist is rewritten, it's mtime must be larger than the old
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen one's. Use utime() before rename() if needed. Note that inode checking
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen wouldn't have been sufficient as inode numbers can be reused.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen This file is usually read the first time you need to know filename for
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen given UID. After that it's not re-read unless new mails come that we
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen don't know about.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen broken clients
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen --------------
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen Originally the middle identifier in Maildir filename was specified
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen only as <process id>_<delivery counter>. That however created a
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen problem with randomized PIDs which made it possible that the same
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen PID was reused within one second.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen So if within one second a mail was delivered, MUA moved it to cur/
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen and another mail was delivered by a new process using same PID as
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen the first one, we likely ended up overwriting the first mail when
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen the second mail was moved over it.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen Nowadays everyone should be giving a bit more specific identifier,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen for example include microseconds in it which Dovecot does.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen There's a simple way to prevent this from happening in some cases:
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen Don't move the mail from new/ to cur/ if it's mtime is >= time() -
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen MAILDIR_SYNC_SECS. The second delivery's link() call then fails
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen because the file is already in new/, and it will then use a
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen different filename. There's a few problems with this however:
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen - it requires extra stat() call which is unneeded extra I/O
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen - another MUA might still move the mail to cur/
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen - if first file's flags are modified by either Dovecot or another
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen MUA, it's moved to cur/ (you _could_ just do the dirty-flagging
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen but that'd be ugly)
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen Because this is useful only for very few people and it requires
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen extra I/O, I decided not to implement this. It should be however
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen quite easy to do since we need to be able to deal with files in new/
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen in any case.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen It's also possible to never accidentally overwrite a mail by using
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen link() + unlink() rather than rename(). This however isn't very
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen good idea as it introduces potential race conditions when multiple
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen clients are accessing the mailbox:
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen Trying to move the same mail from new/ to cur/ at the same time:
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen a) Client 1 uses slightly different filename than client 2,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen for example one sets read-flag on but the other doesn't.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen You have the same mail duplicated now.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen b) Client 3 sees the mail between Client 1's and 2's link() calls
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen and changes it's flag. You have the same mail duplicated now.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen And it gets worse when they're unlink()ing in cur/ directory:
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen c) Client 1 changes mails's flag and client 2 changes it back
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen between 1's link() and unlink(). The mail is now expunged.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen d) If you try to deal with the duplicates by unlink()ing another
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen one of them, you might end up unlinking both of them.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen So, what should we do then if we notice a duplicate? First of all,
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen it might not be a duplicate at all, readdir() might have just
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen returned it twice because it was just renamed. What we should do is
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen create a completely new base name for it and rename() it to that.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen If the call fails with ENOENT, it only means that it wasn't a
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen duplicate after all.
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen*/
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "ioloop.h"
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen#include "array.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "buffer.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "hash.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "str.h"
4dc8837ab37c1a606add1067e21ed868754db4e3Timo Sirainen#include "eacces-error.h"
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainen#include "nfs-workarounds.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "maildir-storage.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "maildir-uidlist.h"
e8bdf1be00aec45d0c6dd72ad9c8be02a3dfc778Timo Sirainen#include "maildir-filename.h"
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen#include "maildir-sync.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <stdio.h>
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen#include <stddef.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <unistd.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <dirent.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <sys/stat.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define MAILDIR_FILENAME_FLAG_FOUND 128
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen/* When rename()ing many files from new/ to cur/, it's possible that next
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen readdir() skips some files. we don't of course wish to lose them, so we
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen go and rescan the new/ directory again from beginning until no files are
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen left. This value is just an optimization to avoid checking the directory
19557f192d37cd54a1a090a8a26d9d47265e4413Aki Tuomi twice needlessly. usually only NFS is the problem case. 1 is the safest
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen bet here, but I guess 5 will do just fine too. */
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen#define MAILDIR_RENAME_RESCAN_COUNT 5
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen
cbf7138b49d32fce0645dc6523fbb42cc07cb2faTimo Sirainen/* This is mostly to avoid infinite looping when rename() destination already
cbf7138b49d32fce0645dc6523fbb42cc07cb2faTimo Sirainen exists as the hard link of the file itself. */
b9b48aaaebf6f72dfab567cda073cde8a7b26598Timo Sirainen#define MAILDIR_SCAN_DIR_MAX_COUNT 5
b9b48aaaebf6f72dfab567cda073cde8a7b26598Timo Sirainen
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen#define DUPE_LINKS_DELETE_SECS 30
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainenenum maildir_scan_why {
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen WHY_FORCED = 0x01,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen WHY_FIRSTSYNC = 0x02,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen WHY_NEWCHANGED = 0x04,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen WHY_CURCHANGED = 0x08,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen WHY_DROPRECENT = 0x10,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen WHY_FINDRECENT = 0x20,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen WHY_DELAYEDNEW = 0x40,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen WHY_DELAYEDCUR = 0x80
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen};
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct maildir_sync_context {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen struct maildir_mailbox *mbox;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *new_dir, *cur_dir;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
e30b748edcef3cf3352478bf21fa8f785bdc773aTimo Sirainen enum mailbox_sync_flags flags;
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen time_t last_touch, last_notify;
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen struct maildir_uidlist_sync_ctx *uidlist_sync_ctx;
1582b4d531679849bba299c17b6ec9402b7df67dTimo Sirainen struct maildir_index_sync_context *index_sync_ctx;
1582b4d531679849bba299c17b6ec9402b7df67dTimo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool partial:1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool locked:1;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool racing:1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen};
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainenvoid maildir_sync_set_racing(struct maildir_sync_context *ctx)
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen{
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen ctx->racing = TRUE;
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen}
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen
9c7e765845357342923e16351181091028e5930fTimo Sirainenvoid maildir_sync_notify(struct maildir_sync_context *ctx)
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen{
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen time_t now;
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen
6bf1543bb7af03324c04e8f9ac8e430f395989aeTimo Sirainen if (ctx == NULL) {
6bf1543bb7af03324c04e8f9ac8e430f395989aeTimo Sirainen /* we got here from maildir-save.c. it has no
6bf1543bb7af03324c04e8f9ac8e430f395989aeTimo Sirainen maildir_sync_context, */
6bf1543bb7af03324c04e8f9ac8e430f395989aeTimo Sirainen return;
6bf1543bb7af03324c04e8f9ac8e430f395989aeTimo Sirainen }
6bf1543bb7af03324c04e8f9ac8e430f395989aeTimo Sirainen
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen now = time(NULL);
b00787191c3c31bebb939c3d00f1fcdb67356c69Timo Sirainen if (now - ctx->last_touch > MAILDIR_LOCK_TOUCH_SECS && ctx->locked) {
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen (void)maildir_uidlist_lock_touch(ctx->mbox->uidlist);
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen ctx->last_touch = now;
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen }
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen if (now - ctx->last_notify > MAIL_STORAGE_STAYALIVE_SECS) {
d22301419109ed4a38351715e6760011421dadecTimo Sirainen struct mailbox *box = &ctx->mbox->box;
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen
ee116df08d0fdab703483e18fe8076b2ef9fd9d7Timo Sirainen if (box->storage->callbacks.notify_ok != NULL) {
ee116df08d0fdab703483e18fe8076b2ef9fd9d7Timo Sirainen box->storage->callbacks.
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen notify_ok(box, "Hang in there..",
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen box->storage->callback_context);
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen }
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen ctx->last_notify = now;
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen }
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen}
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic struct maildir_sync_context *
e30b748edcef3cf3352478bf21fa8f785bdc773aTimo Sirainenmaildir_sync_context_new(struct maildir_mailbox *mbox,
e30b748edcef3cf3352478bf21fa8f785bdc773aTimo Sirainen enum mailbox_sync_flags flags)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct maildir_sync_context *ctx;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ctx = t_new(struct maildir_sync_context, 1);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen ctx->mbox = mbox;
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen ctx->new_dir = t_strconcat(mailbox_get_path(&mbox->box), "/new", NULL);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen ctx->cur_dir = t_strconcat(mailbox_get_path(&mbox->box), "/cur", NULL);
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen ctx->last_touch = ioloop_time;
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen ctx->last_notify = ioloop_time;
e30b748edcef3cf3352478bf21fa8f785bdc773aTimo Sirainen ctx->flags = flags;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return ctx;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void maildir_sync_deinit(struct maildir_sync_context *ctx)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ctx->uidlist_sync_ctx != NULL)
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen (void)maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx, FALSE);
ea9fd7f876643e985946a2563140359064819b8eTimo Sirainen if (ctx->index_sync_ctx != NULL)
ea9fd7f876643e985946a2563140359064819b8eTimo Sirainen maildir_sync_index_rollback(&ctx->index_sync_ctx);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainenstatic int maildir_fix_duplicate(struct maildir_sync_context *ctx,
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen const char *dir, const char *fname2)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen const char *fname1, *path1, *path2;
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen const char *new_fname, *new_path;
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen struct stat st1, st2;
e0719fca14e337eee5a0d924bc4e9d53151a7188Timo Sirainen uoff_t size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen fname1 = maildir_uidlist_sync_get_full_filename(ctx->uidlist_sync_ctx,
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen fname2);
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen i_assert(fname1 != NULL);
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen path1 = t_strconcat(dir, "/", fname1, NULL);
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen path2 = t_strconcat(dir, "/", fname2, NULL);
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen if (stat(path1, &st1) < 0 || stat(path2, &st2) < 0) {
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen /* most likely the files just don't exist anymore.
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen don't really care about other errors much. */
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen return 0;
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen }
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen if (st1.st_ino == st2.st_ino &&
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen CMP_DEV_T(st1.st_dev, st2.st_dev)) {
cbf7138b49d32fce0645dc6523fbb42cc07cb2faTimo Sirainen /* Files are the same. this means either a race condition
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen between stat() calls, or that the files were link()ed. */
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen if (st1.st_nlink > 1 && st2.st_nlink == st1.st_nlink &&
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen st1.st_ctime == st2.st_ctime &&
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen st1.st_ctime < ioloop_time - DUPE_LINKS_DELETE_SECS) {
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen /* The file has hard links and it hasn't had any
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen changes (such as renames) for a while, so this
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen isn't a race condition.
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen rename()ing one file on top of the other would fix
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen this safely, except POSIX decided that rename()
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen doesn't work that way. So we'll have unlink() one
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen and hope that another process didn't just decide to
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen unlink() the other (uidlist lock prevents this from
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen happening) */
275cc4c040899c132b2acbe2fcac48ba4c1abbcfTimo Sirainen if (i_unlink(path2) == 0)
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen i_warning("Unlinked a duplicate: %s", path2);
992a9e2d6c6ee45d87089ac54267e0198a7802c3Timo Sirainen }
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen return 0;
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen }
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen
538303a216166f3526c0ae9658c9978275cfa100Timo Sirainen new_fname = maildir_filename_generate();
e0719fca14e337eee5a0d924bc4e9d53151a7188Timo Sirainen /* preserve S= and W= sizes if they're available.
e0719fca14e337eee5a0d924bc4e9d53151a7188Timo Sirainen (S=size is required for zlib plugin to work) */
e0719fca14e337eee5a0d924bc4e9d53151a7188Timo Sirainen if (maildir_filename_get_size(fname2, MAILDIR_EXTRA_FILE_SIZE, &size)) {
e0719fca14e337eee5a0d924bc4e9d53151a7188Timo Sirainen new_fname = t_strdup_printf("%s,%c=%"PRIuUOFF_T,
e0719fca14e337eee5a0d924bc4e9d53151a7188Timo Sirainen new_fname, MAILDIR_EXTRA_FILE_SIZE, size);
e0719fca14e337eee5a0d924bc4e9d53151a7188Timo Sirainen }
e0719fca14e337eee5a0d924bc4e9d53151a7188Timo Sirainen if (maildir_filename_get_size(fname2, MAILDIR_EXTRA_VIRTUAL_SIZE, &size)) {
e0719fca14e337eee5a0d924bc4e9d53151a7188Timo Sirainen new_fname = t_strdup_printf("%s,%c=%"PRIuUOFF_T,
e0719fca14e337eee5a0d924bc4e9d53151a7188Timo Sirainen new_fname, MAILDIR_EXTRA_VIRTUAL_SIZE, size);
e0719fca14e337eee5a0d924bc4e9d53151a7188Timo Sirainen }
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen new_path = t_strconcat(mailbox_get_path(&ctx->mbox->box),
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen "/new/", new_fname, NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen if (rename(path2, new_path) == 0)
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen i_warning("Fixed a duplicate: %s -> %s", path2, new_fname);
a87e5f15283e057c7dc26dd9db7b616268c95ca7Timo Sirainen else if (errno != ENOENT) {
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(&ctx->mbox->box,
c93cd163f9c1d4b0ca29f49cbfdbf474caeef5bfTimo Sirainen "Couldn't fix a duplicate: rename(%s, %s) failed: %m",
6925fd9cd70c30884406d50f1d85efb6561e776cTimo Sirainen path2, new_path);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainenstatic int
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainenmaildir_rename_empty_basename(struct maildir_sync_context *ctx,
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen const char *dir, const char *fname)
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen{
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen const char *old_path, *new_fname, *new_path;
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen old_path = t_strconcat(dir, "/", fname, NULL);
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen new_fname = maildir_filename_generate();
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen new_path = t_strconcat(mailbox_get_path(&ctx->mbox->box),
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen "/new/", new_fname, NULL);
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen if (rename(old_path, new_path) == 0)
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen i_warning("Fixed broken filename: %s -> %s", old_path, new_fname);
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen else if (errno != ENOENT) {
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(&ctx->mbox->box,
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen "Couldn't fix a broken filename: rename(%s, %s) failed: %m",
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen old_path, new_path);
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen return -1;
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen }
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen return 0;
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen}
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainenstatic int
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainenmaildir_stat(struct maildir_mailbox *mbox, const char *path, struct stat *st_r)
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainen{
d22301419109ed4a38351715e6760011421dadecTimo Sirainen struct mailbox *box = &mbox->box;
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen int i;
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen for (i = 0;; i++) {
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen if (nfs_safe_stat(path, st_r) == 0)
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen return 0;
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT)
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen break;
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen if (!maildir_set_deleted(box))
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen return -1;
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen /* try again */
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainen }
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainen
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(box, "stat(%s) failed: %m", path);
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainen return -1;
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainen}
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainen
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainenstatic int
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainenmaildir_scan_dir(struct maildir_sync_context *ctx, bool new_dir, bool final,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen enum maildir_scan_why why)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen const char *path;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen DIR *dirp;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen string_t *src, *dest;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct dirent *dp;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen struct stat st;
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen enum maildir_uidlist_rec_flag flags;
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen unsigned int time_diff, i, readdir_count = 0, move_count = 0;
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen time_t start_time;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen int ret = 1;
8bb360f9e5de1c25e4f875205bb06e8bf15dae14Timo Sirainen bool move_new, dir_changed = FALSE;
d3eff05aaa4c2bc0a7580ee87a54f6693f4a8241Timo Sirainen
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen path = new_dir ? ctx->new_dir : ctx->cur_dir;
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen for (i = 0;; i++) {
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen dirp = opendir(path);
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen if (dirp != NULL)
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen break;
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT) {
4dc8837ab37c1a606add1067e21ed868754db4e3Timo Sirainen if (errno == EACCES) {
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(&ctx->mbox->box, "%s",
4dc8837ab37c1a606add1067e21ed868754db4e3Timo Sirainen eacces_error_get("opendir", path));
4dc8837ab37c1a606add1067e21ed868754db4e3Timo Sirainen } else {
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(&ctx->mbox->box,
4dc8837ab37c1a606add1067e21ed868754db4e3Timo Sirainen "opendir(%s) failed: %m", path);
4dc8837ab37c1a606add1067e21ed868754db4e3Timo Sirainen }
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainen return -1;
8e5fedd9ada47735be8ac0f8af2a66e8528bd776Timo Sirainen }
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (!maildir_set_deleted(&ctx->mbox->box))
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen return -1;
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen /* try again */
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen }
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen#ifdef HAVE_DIRFD
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (fstat(dirfd(dirp), &st) < 0) {
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(&ctx->mbox->box,
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen "fstat(%s) failed: %m", path);
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen (void)closedir(dirp);
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen return -1;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen }
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen#else
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (maildir_stat(ctx->mbox, path, &st) < 0) {
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen (void)closedir(dirp);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen#endif
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen start_time = time(NULL);
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (new_dir) {
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen ctx->mbox->maildir_hdr.new_check_time = start_time;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen ctx->mbox->maildir_hdr.new_mtime = st.st_mtime;
b3bb775c6b735a7f6021dea799601fbfdb656e58Timo Sirainen ctx->mbox->maildir_hdr.new_mtime_nsecs = ST_MTIME_NSEC(st);
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen } else {
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen ctx->mbox->maildir_hdr.cur_check_time = start_time;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen ctx->mbox->maildir_hdr.cur_mtime = st.st_mtime;
b3bb775c6b735a7f6021dea799601fbfdb656e58Timo Sirainen ctx->mbox->maildir_hdr.cur_mtime_nsecs = ST_MTIME_NSEC(st);
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen src = t_str_new(1024);
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen dest = t_str_new(1024);
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen
0dfacbe4cb9a16c0cdaed535a9ce2bae8e25e4f0Timo Sirainen move_new = new_dir && ctx->locked &&
0dfacbe4cb9a16c0cdaed535a9ce2bae8e25e4f0Timo Sirainen ((ctx->mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) != 0 ||
0dfacbe4cb9a16c0cdaed535a9ce2bae8e25e4f0Timo Sirainen ctx->mbox->storage->set->maildir_empty_new);
41264e5dcef8335ab7ba422822b3ab518b7a327aTimo Sirainen
41264e5dcef8335ab7ba422822b3ab518b7a327aTimo Sirainen errno = 0;
41264e5dcef8335ab7ba422822b3ab518b7a327aTimo Sirainen for (; (dp = readdir(dirp)) != NULL; errno = 0) {
7bd6001d84ecc1792ddfd54fe8efa63c509d90b1Timo Sirainen if (dp->d_name[0] == '.')
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen continue;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen if (dp->d_name[0] == MAILDIR_INFO_SEP) {
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen /* don't even try to use file with empty base name */
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen if (maildir_rename_empty_basename(ctx, path,
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen dp->d_name) < 0)
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen break;
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen continue;
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen }
b63284468d717737ecd63d78b6928c5d7f0d3634Timo Sirainen
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen flags = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (move_new) {
9a56220167d02bbcb66a81b7553f4eb4da939945Timo Sirainen i_assert(dp->d_name[0] != '\0');
9a56220167d02bbcb66a81b7553f4eb4da939945Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_truncate(src, 0);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_truncate(dest, 0);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_printfa(src, "%s/%s", ctx->new_dir, dp->d_name);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_printfa(dest, "%s/%s", ctx->cur_dir, dp->d_name);
8754bb7a1f24705ffa5434f9e10d57e0b3b88d6eTimo Sirainen if (strchr(dp->d_name, MAILDIR_INFO_SEP) == NULL) {
8754bb7a1f24705ffa5434f9e10d57e0b3b88d6eTimo Sirainen str_append(dest, MAILDIR_FLAGS_FULL_SEP);
8754bb7a1f24705ffa5434f9e10d57e0b3b88d6eTimo Sirainen }
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen if (rename(str_c(src), str_c(dest)) == 0) {
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen /* we moved it - it's \Recent for us */
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen dir_changed = TRUE;
b60baf6af900a610b2b2ddd24a46f8311acc3386Timo Sirainen move_count++;
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen flags |= MAILDIR_UIDLIST_REC_FLAG_MOVED |
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen MAILDIR_UIDLIST_REC_FLAG_RECENT;
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen } else if (ENOTFOUND(errno)) {
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen /* someone else moved it already */
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen dir_changed = TRUE;
b60baf6af900a610b2b2ddd24a46f8311acc3386Timo Sirainen move_count++;
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen flags |= MAILDIR_UIDLIST_REC_FLAG_MOVED |
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen MAILDIR_UIDLIST_REC_FLAG_RECENT;
9a099a65160987349f441c82ab0e38f32b747adbTimo Sirainen } else if (ENOSPACE(errno) || errno == EACCES) {
9a099a65160987349f441c82ab0e38f32b747adbTimo Sirainen /* not enough disk space / read-only maildir,
9a099a65160987349f441c82ab0e38f32b747adbTimo Sirainen leave here */
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen move_new = FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else {
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(&ctx->mbox->box,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "rename(%s, %s) failed: %m",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_c(src), str_c(dest));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
b60baf6af900a610b2b2ddd24a46f8311acc3386Timo Sirainen if ((move_count % MAILDIR_SLOW_MOVE_COUNT) == 0)
b60baf6af900a610b2b2ddd24a46f8311acc3386Timo Sirainen maildir_sync_notify(ctx);
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen } else if (new_dir) {
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen MAILDIR_UIDLIST_REC_FLAG_RECENT;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen readdir_count++;
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen if ((readdir_count % MAILDIR_SLOW_CHECK_COUNT) == 0)
b60baf6af900a610b2b2ddd24a46f8311acc3386Timo Sirainen maildir_sync_notify(ctx);
d3eff05aaa4c2bc0a7580ee87a54f6693f4a8241Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = maildir_uidlist_sync_next(ctx->uidlist_sync_ctx,
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen dp->d_name, flags);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ret <= 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ret < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen break;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* possibly duplicate - try fixing it */
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen T_BEGIN {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen ret = maildir_fix_duplicate(ctx, path,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen dp->d_name);
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen } T_END;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (ret < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen break;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainen#ifdef __APPLE__
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainen if (errno == EINVAL && move_count > 0 && !final) {
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainen /* OS X HFS+: readdir() fails sometimes when rename()
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainen have been done. */
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainen move_count = MAILDIR_RENAME_RESCAN_COUNT + 1;
eae1d6e75713d3d658908ac39b719992e2f8a456Timo Sirainen } else
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainen#endif
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainen
41264e5dcef8335ab7ba422822b3ab518b7a327aTimo Sirainen if (errno != 0) {
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(&ctx->mbox->box,
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi "readdir(%s) failed: %m", path);
41264e5dcef8335ab7ba422822b3ab518b7a327aTimo Sirainen ret = -1;
41264e5dcef8335ab7ba422822b3ab518b7a327aTimo Sirainen }
41264e5dcef8335ab7ba422822b3ab518b7a327aTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (closedir(dirp) < 0) {
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(&ctx->mbox->box,
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi "closedir(%s) failed: %m", path);
41264e5dcef8335ab7ba422822b3ab518b7a327aTimo Sirainen ret = -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (dir_changed) {
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen /* save the exact new times. the new mtimes should be >=
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen "start_time", but just in case something weird happens and
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen mtime doesn't update, use "start_time". */
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen if (stat(ctx->new_dir, &st) == 0) {
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen ctx->mbox->maildir_hdr.new_check_time =
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen I_MAX(st.st_mtime, start_time);
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen ctx->mbox->maildir_hdr.new_mtime = st.st_mtime;
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen ctx->mbox->maildir_hdr.new_mtime_nsecs =
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen ST_MTIME_NSEC(st);
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen }
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen if (stat(ctx->cur_dir, &st) == 0) {
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen ctx->mbox->maildir_hdr.new_check_time =
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen I_MAX(st.st_mtime, start_time);
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen ctx->mbox->maildir_hdr.cur_mtime = st.st_mtime;
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen ctx->mbox->maildir_hdr.cur_mtime_nsecs =
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen ST_MTIME_NSEC(st);
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen }
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen }
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen time_diff = time(NULL) - start_time;
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen if (time_diff >= MAILDIR_SYNC_TIME_WARN_SECS) {
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen i_warning("Maildir: Scanning %s took %u seconds "
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen "(%u readdir()s, %u rename()s to cur/, why=0x%x)",
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen path, time_diff, readdir_count, move_count, why);
43955c82f9f52c969c777b3da00bc170183dfdf2Timo Sirainen }
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen
44dc970b18c4e2d06f34cb908924152156e4a45bTimo Sirainen return ret < 0 ? -1 :
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainen (move_count <= MAILDIR_RENAME_RESCAN_COUNT || final ? 0 : 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainenstatic void maildir_sync_get_header(struct maildir_mailbox *mbox)
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen{
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen const void *data;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen size_t data_size;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen
d22301419109ed4a38351715e6760011421dadecTimo Sirainen mail_index_get_header_ext(mbox->box.view, mbox->maildir_ext_id,
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen &data, &data_size);
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (data_size == 0) {
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen /* header doesn't exist */
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen } else {
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen memcpy(&mbox->maildir_hdr, data,
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen I_MIN(sizeof(mbox->maildir_hdr), data_size));
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen }
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen}
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainenint maildir_sync_header_refresh(struct maildir_mailbox *mbox)
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen{
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen if (mail_index_refresh(mbox->box.index) < 0) {
1f1ee8db68d9ae1604350801cd8dc33ebe29fe8aTimo Sirainen mailbox_set_index_error(&mbox->box);
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen return -1;
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen }
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen maildir_sync_get_header(mbox);
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen return 0;
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen}
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen
7c449f545b10daa47027552f98d916a9805da662Timo Sirainenstatic int maildir_sync_quick_check(struct maildir_mailbox *mbox, bool undirty,
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen const char *new_dir, const char *cur_dir,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen bool *new_changed_r, bool *cur_changed_r,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen enum maildir_scan_why *why_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen#define DIR_DELAYED_REFRESH(hdr, name) \
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen ((hdr)->name ## _check_time <= \
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen (hdr)->name ## _mtime + MAILDIR_SYNC_SECS && \
7c449f545b10daa47027552f98d916a9805da662Timo Sirainen (undirty || \
7c449f545b10daa47027552f98d916a9805da662Timo Sirainen (time_t)(hdr)->name ## _check_time < ioloop_time - MAILDIR_SYNC_SECS))
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen#define DIR_MTIME_CHANGED(st, hdr, name) \
ff4bb2dfb5714eeb0408d3bb862de1646351d097Timo Sirainen ((st).st_mtime != (time_t)(hdr)->name ## _mtime || \
b3bb775c6b735a7f6021dea799601fbfdb656e58Timo Sirainen !ST_NTIMES_EQUAL(ST_MTIME_NSEC(st), (hdr)->name ## _mtime_nsecs))
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen struct maildir_index_header *hdr = &mbox->maildir_hdr;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen struct stat new_st, cur_st;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen bool refreshed = FALSE, check_new = FALSE, check_cur = FALSE;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen *why_r = 0;
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (mbox->maildir_hdr.new_mtime == 0) {
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen maildir_sync_get_header(mbox);
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (mbox->maildir_hdr.new_mtime == 0) {
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen /* first sync */
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen *why_r |= WHY_FIRSTSYNC;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen *new_changed_r = *cur_changed_r = TRUE;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen return 0;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen }
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *new_changed_r = *cur_changed_r = FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen /* try to avoid stat()ing by first checking delayed changes */
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (DIR_DELAYED_REFRESH(hdr, new) ||
fe813f74aaccb12f38e1bd9cd338c6a37fa646e5Timo Sirainen (DIR_DELAYED_REFRESH(hdr, cur) &&
fe813f74aaccb12f38e1bd9cd338c6a37fa646e5Timo Sirainen !mbox->storage->set->maildir_very_dirty_syncs)) {
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen /* refresh index and try again */
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen if (maildir_sync_header_refresh(mbox) < 0)
60f9b96be55e63f0113e273dda8ba3b883c6f095Timo Sirainen return -1;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen refreshed = TRUE;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen if (DIR_DELAYED_REFRESH(hdr, new)) {
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen *why_r |= WHY_DELAYEDNEW;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen *new_changed_r = TRUE;
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen }
fe813f74aaccb12f38e1bd9cd338c6a37fa646e5Timo Sirainen if (DIR_DELAYED_REFRESH(hdr, cur) &&
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen !mbox->storage->set->maildir_very_dirty_syncs) {
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen *why_r |= WHY_DELAYEDCUR;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen *cur_changed_r = TRUE;
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen }
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (*new_changed_r && *cur_changed_r)
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen return 0;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (!*new_changed_r) {
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (maildir_stat(mbox, new_dir, &new_st) < 0)
bde6382cf65fba6165dc3603f5419e194d87f404Timo Sirainen return -1;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen check_new = TRUE;
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen }
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (!*cur_changed_r) {
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if (maildir_stat(mbox, cur_dir, &cur_st) < 0)
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen return -1;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen check_cur = TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen for (;;) {
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen if (check_new) {
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen *new_changed_r = DIR_MTIME_CHANGED(new_st, hdr, new);
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen if (*new_changed_r)
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen *why_r |= WHY_NEWCHANGED;
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen }
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen if (check_cur) {
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen *cur_changed_r = DIR_MTIME_CHANGED(cur_st, hdr, cur);
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen if (*cur_changed_r)
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen *why_r |= WHY_CURCHANGED;
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen }
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen if ((!*new_changed_r && !*cur_changed_r) || refreshed)
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen break;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen /* refresh index and try again */
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen if (maildir_sync_header_refresh(mbox) < 0)
60f9b96be55e63f0113e273dda8ba3b883c6f095Timo Sirainen return -1;
d61a5e0e4ff58d1aa613f0d51161e5bb0f092514Timo Sirainen refreshed = TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainenstatic void maildir_sync_update_next_uid(struct maildir_mailbox *mbox)
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen{
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen const struct mail_index_header *hdr;
8bb360f9e5de1c25e4f875205bb06e8bf15dae14Timo Sirainen uint32_t uid_validity;
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen
d22301419109ed4a38351715e6760011421dadecTimo Sirainen hdr = mail_index_get_header(mbox->box.view);
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen if (hdr->uid_validity == 0)
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen return;
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen uid_validity = maildir_uidlist_get_uid_validity(mbox->uidlist);
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen if (uid_validity == hdr->uid_validity || uid_validity == 0) {
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen /* make sure uidlist's next_uid is at least as large as
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen index file's. typically this happens only if uidlist gets
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen deleted. */
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen maildir_uidlist_set_uid_validity(mbox->uidlist,
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen hdr->uid_validity);
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen maildir_uidlist_set_next_uid(mbox->uidlist,
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen hdr->next_uid, FALSE);
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen }
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen}
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainenstatic bool
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainenhave_recent_messages(struct maildir_sync_context *ctx, bool seen_changes)
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen{
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen const struct mail_index_header *hdr;
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainen uint32_t next_uid;
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen
d22301419109ed4a38351715e6760011421dadecTimo Sirainen hdr = mail_index_get_header(ctx->mbox->box.view);
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainen if (!seen_changes) {
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainen /* index is up to date. get the next-uid from it */
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainen next_uid = hdr->next_uid;
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainen } else {
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainen (void)maildir_uidlist_refresh(ctx->mbox->uidlist);
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainen next_uid = maildir_uidlist_get_next_uid(ctx->mbox->uidlist);
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainen }
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainen return hdr->first_recent_uid < next_uid;
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen}
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainenstatic int maildir_sync_get_changes(struct maildir_sync_context *ctx,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen bool *new_changed_r, bool *cur_changed_r,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen enum maildir_scan_why *why_r)
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen{
ced118ac5caf6fe83d34339c2c65c63b2aa768acTimo Sirainen struct maildir_mailbox *mbox = ctx->mbox;
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen enum mail_index_sync_flags flags = 0;
7c449f545b10daa47027552f98d916a9805da662Timo Sirainen bool undirty = (ctx->flags & MAILBOX_SYNC_FLAG_FULL_READ) != 0;
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen *why_r = 0;
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen
ced118ac5caf6fe83d34339c2c65c63b2aa768acTimo Sirainen if (maildir_sync_quick_check(mbox, undirty, ctx->new_dir, ctx->cur_dir,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen new_changed_r, cur_changed_r, why_r) < 0)
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen return -1;
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainen /* if there are files in new/, we'll need to move them. we'll check
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainen this by seeing if we have any recent messages */
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen if ((mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) != 0) {
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen if (!*new_changed_r && have_recent_messages(ctx, FALSE)) {
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen *new_changed_r = TRUE;
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen *why_r |= WHY_DROPRECENT;
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen }
8d889b6d842e96ecbe7b6493920bbb6df8e0ed30Timo Sirainen } else if (*new_changed_r) {
0ed9ccd0047f75df54a49bc117ca301eb398e447Timo Sirainen /* if recent messages have been externally deleted from new/,
8d889b6d842e96ecbe7b6493920bbb6df8e0ed30Timo Sirainen we need to get them out of index. this requires that
8d889b6d842e96ecbe7b6493920bbb6df8e0ed30Timo Sirainen we make sure they weren't just moved to cur/. */
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen if (!*cur_changed_r && have_recent_messages(ctx, TRUE)) {
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen *cur_changed_r = TRUE;
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen *why_r |= WHY_FINDRECENT;
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen }
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen }
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen
7d7f4648f72b8c70928e04514b0d93dad0ba6fd5Timo Sirainen if (*new_changed_r || *cur_changed_r)
7d7f4648f72b8c70928e04514b0d93dad0ba6fd5Timo Sirainen return 1;
7d7f4648f72b8c70928e04514b0d93dad0ba6fd5Timo Sirainen
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen if ((mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) != 0)
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen flags |= MAIL_INDEX_SYNC_FLAG_DROP_RECENT;
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen if (mbox->synced) {
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen /* refresh index only after the first sync, i.e. avoid wasting
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen time on refreshing it immediately after it was just opened */
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen mail_index_refresh(mbox->box.index);
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen }
d22301419109ed4a38351715e6760011421dadecTimo Sirainen return mail_index_sync_have_any(mbox->box.index, flags) ? 1 : 0;
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen}
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenstatic int ATTR_NULL(3)
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenmaildir_sync_context(struct maildir_sync_context *ctx, bool forced,
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen uint32_t *find_uid, bool *lost_files_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
649823f9c7e42a424a8fadc427e12f3576c4f714Timo Sirainen enum maildir_uidlist_sync_flags sync_flags;
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen enum maildir_uidlist_rec_flag flags;
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen bool new_changed, cur_changed, lock_failure;
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen const char *fname;
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen enum maildir_scan_why why;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen int ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen *lost_files_r = FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen if (forced) {
a38ef15060e45e5060bb24c403a580b9a57a818cTimo Sirainen new_changed = cur_changed = TRUE;
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen why = WHY_FORCED;
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen } else {
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen ret = maildir_sync_get_changes(ctx, &new_changed, &cur_changed,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen &why);
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen if (ret <= 0)
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen return ret;
a38ef15060e45e5060bb24c403a580b9a57a818cTimo Sirainen }
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen /*
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen Locking, locking, locking.. Wasn't maildir supposed to be lockless?
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen We can get here either as beginning a real maildir sync, or when
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen committing changes to maildir but a file was lost (maybe renamed).
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen So, we're going to need two locks. One for index and one for
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen uidlist. To avoid deadlocking do the uidlist lock always first.
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen uidlist is needed only for figuring out UIDs for newly seen files,
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen so theoretically we wouldn't need to lock it unless there are new
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen files. It has a few problems though, assuming the index lock didn't
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen already protect it (eg. in-memory indexes):
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen 1. Just because you see a new file which doesn't exist in uidlist
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen file, doesn't mean that the file really exists anymore, or that
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen your readdir() lists all new files. Meaning that this is possible:
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen A: opendir(), readdir() -> new file ...
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen -- new files are written to the maildir --
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen B: opendir(), readdir() -> new file, lock uidlist,
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen readdir() -> another new file, rewrite uidlist, unlock
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen A: ... lock uidlist, readdir() -> nothing left, rewrite uidlist,
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen unlock
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen The second time running A didn't see the two new files. To handle
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen this correctly, it must not remove the new unseen files from
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen uidlist. This is possible to do, but adds extra complexity.
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen 2. If another process is rename()ing files while we are
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen readdir()ing, it's possible that readdir() never lists some files,
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen causing Dovecot to assume they were expunged. In next sync they
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen would show up again, but client could have already been notified of
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen that and they would show up under new UIDs, so the damage is
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen already done.
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen Both of the problems can be avoided if we simply lock the uidlist
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen before syncing and keep it until sync is finished. Typically this
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen would happen in any case, as there is the index lock..
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen The second case is still a problem with external changes though,
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen because maildir doesn't require any kind of locking. Luckily this
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen problem rarely happens except under high amount of modifications.
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen */
65d6ca3fb5450b81df0190d9e9aa62c00fed5116Timo Sirainen
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen if (!cur_changed) {
1582b4d531679849bba299c17b6ec9402b7df67dTimo Sirainen ctx->partial = TRUE;
1582b4d531679849bba299c17b6ec9402b7df67dTimo Sirainen sync_flags = MAILDIR_UIDLIST_SYNC_PARTIAL;
1582b4d531679849bba299c17b6ec9402b7df67dTimo Sirainen } else {
1582b4d531679849bba299c17b6ec9402b7df67dTimo Sirainen ctx->partial = FALSE;
1582b4d531679849bba299c17b6ec9402b7df67dTimo Sirainen sync_flags = 0;
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen if (forced)
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen sync_flags |= MAILDIR_UIDLIST_SYNC_FORCE;
e30b748edcef3cf3352478bf21fa8f785bdc773aTimo Sirainen if ((ctx->flags & MAILBOX_SYNC_FLAG_FAST) != 0)
e30b748edcef3cf3352478bf21fa8f785bdc773aTimo Sirainen sync_flags |= MAILDIR_UIDLIST_SYNC_TRYLOCK;
1582b4d531679849bba299c17b6ec9402b7df67dTimo Sirainen }
649823f9c7e42a424a8fadc427e12f3576c4f714Timo Sirainen ret = maildir_uidlist_sync_init(ctx->mbox->uidlist, sync_flags,
57a91f930a12d2cd1220da4f3f7cb2c47557cd37Timo Sirainen &ctx->uidlist_sync_ctx);
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen lock_failure = ret <= 0;
57a91f930a12d2cd1220da4f3f7cb2c47557cd37Timo Sirainen if (ret <= 0) {
d22301419109ed4a38351715e6760011421dadecTimo Sirainen struct mail_storage *storage = ctx->mbox->box.storage;
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen if (ret == 0) {
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen /* timeout */
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen return 0;
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen }
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen /* locking failed. sync anyway without locking so that it's
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen possible to expunge messages when out of quota. */
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen if (forced) {
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen /* we're already forcing a sync, we're trying to find
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen a message that was probably already expunged, don't
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen loop for a long time trying to find it. */
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen return -1;
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen }
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen ret = maildir_uidlist_sync_init(ctx->mbox->uidlist, sync_flags |
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen MAILDIR_UIDLIST_SYNC_NOLOCK,
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen &ctx->uidlist_sync_ctx);
305465bb1a4c5d90c4b4e2c2790eb05fa4ebc41eTimo Sirainen if (ret <= 0) {
305465bb1a4c5d90c4b4e2c2790eb05fa4ebc41eTimo Sirainen i_assert(ret != 0);
305465bb1a4c5d90c4b4e2c2790eb05fa4ebc41eTimo Sirainen return -1;
305465bb1a4c5d90c4b4e2c2790eb05fa4ebc41eTimo Sirainen }
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen
ee116df08d0fdab703483e18fe8076b2ef9fd9d7Timo Sirainen if (storage->callbacks.notify_no != NULL) {
d22301419109ed4a38351715e6760011421dadecTimo Sirainen storage->callbacks.notify_no(&ctx->mbox->box,
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen "Internal mailbox synchronization failure, "
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen "showing only old mails.",
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen storage->callback_context);
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen }
a38ef15060e45e5060bb24c403a580b9a57a818cTimo Sirainen }
1582b4d531679849bba299c17b6ec9402b7df67dTimo Sirainen ctx->locked = maildir_uidlist_is_locked(ctx->mbox->uidlist);
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen if (!ctx->locked)
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen ctx->partial = TRUE;
73c76fa7340a107229c530196d026aadeae979c7Timo Sirainen
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen if (!ctx->mbox->syncing_commit && (ctx->locked || lock_failure)) {
9c7e765845357342923e16351181091028e5930fTimo Sirainen if (maildir_sync_index_begin(ctx->mbox, ctx,
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen &ctx->index_sync_ctx) < 0)
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen return -1;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen }
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (new_changed || cur_changed) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* if we're going to check cur/ dir our current logic requires
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen that new/ dir is checked as well. it's a good idea anyway. */
b9b48aaaebf6f72dfab567cda073cde8a7b26598Timo Sirainen unsigned int count = 0;
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainen bool final = FALSE;
b9b48aaaebf6f72dfab567cda073cde8a7b26598Timo Sirainen
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen while ((ret = maildir_scan_dir(ctx, TRUE, final, why)) > 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* rename()d at least some files, which might have
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen caused some other files to be missed. check again
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen (see MAILDIR_RENAME_RESCAN_COUNT). */
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainen if (++count >= MAILDIR_SCAN_DIR_MAX_COUNT)
38df0cacce475112991e60d796f8f2105c616f01Timo Sirainen final = TRUE;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (ret < 0)
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen return -1;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (cur_changed) {
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen if (maildir_scan_dir(ctx, FALSE, TRUE, why) < 0)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return -1;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen maildir_sync_update_next_uid(ctx->mbox);
7895c4845da515b0aa9bb156674a1fca40803f44Timo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* finish uidlist syncing, but keep it still locked */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen maildir_uidlist_sync_finish(ctx->uidlist_sync_ctx);
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
7abd00c65bbf53fda3f638101c4cd43647d1eb07Timo Sirainen if (!ctx->locked) {
7abd00c65bbf53fda3f638101c4cd43647d1eb07Timo Sirainen /* make sure we sync the maildir later */
7abd00c65bbf53fda3f638101c4cd43647d1eb07Timo Sirainen ctx->mbox->maildir_hdr.new_mtime = 0;
7abd00c65bbf53fda3f638101c4cd43647d1eb07Timo Sirainen ctx->mbox->maildir_hdr.cur_mtime = 0;
7abd00c65bbf53fda3f638101c4cd43647d1eb07Timo Sirainen }
7abd00c65bbf53fda3f638101c4cd43647d1eb07Timo Sirainen
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen if (ctx->index_sync_ctx != NULL) {
57a91f930a12d2cd1220da4f3f7cb2c47557cd37Timo Sirainen /* NOTE: index syncing here might cause a re-sync due to
57a91f930a12d2cd1220da4f3f7cb2c47557cd37Timo Sirainen files getting lost, so this function might be called
19557f192d37cd54a1a090a8a26d9d47265e4413Aki Tuomi reentrantly. */
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen ret = maildir_sync_index(ctx->index_sync_ctx, ctx->partial);
ea9fd7f876643e985946a2563140359064819b8eTimo Sirainen if (ret < 0)
ea9fd7f876643e985946a2563140359064819b8eTimo Sirainen maildir_sync_index_rollback(&ctx->index_sync_ctx);
ea9fd7f876643e985946a2563140359064819b8eTimo Sirainen else if (maildir_sync_index_commit(&ctx->index_sync_ctx) < 0)
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen return -1;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen if (ret < 0)
65d6ca3fb5450b81df0190d9e9aa62c00fed5116Timo Sirainen return -1;
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen if (ret == 0)
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen *lost_files_r = TRUE;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen i_assert(maildir_uidlist_is_locked(ctx->mbox->uidlist) ||
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen lock_failure);
65d6ca3fb5450b81df0190d9e9aa62c00fed5116Timo Sirainen }
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen if (find_uid != NULL && *find_uid != 0) {
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainen ret = maildir_uidlist_lookup(ctx->mbox->uidlist,
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainen *find_uid, &flags, &fname);
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen if (ret < 0)
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen return -1;
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen if (ret == 0) {
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen /* UID is expunged */
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen *find_uid = 0;
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen } else if ((flags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) == 0) {
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen *find_uid = 0;
815fc205d65f638ad3051cf7a87d320f581e05fdTimo Sirainen } else {
815fc205d65f638ad3051cf7a87d320f581e05fdTimo Sirainen /* we didn't find it, possibly expunged? */
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen }
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen }
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen return maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx, TRUE);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainenint maildir_sync_lookup(struct maildir_mailbox *mbox, uint32_t uid,
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainen enum maildir_uidlist_rec_flag *flags_r,
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainen const char **fname_r)
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainen{
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainen int ret;
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainen
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainen ret = maildir_uidlist_lookup(mbox->uidlist, uid, flags_r, fname_r);
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen if (ret != 0)
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen return ret;
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen if (maildir_uidlist_is_open(mbox->uidlist)) {
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen /* refresh uidlist and check again in case it was added
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen after the last mailbox sync */
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen if (mbox->sync_uidlist_refreshed) {
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen /* we've already refreshed it, don't bother again */
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen return ret;
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainen }
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen mbox->sync_uidlist_refreshed = TRUE;
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen if (maildir_uidlist_refresh(mbox->uidlist) < 0)
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen return -1;
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen } else {
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen /* the uidlist doesn't exist. */
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen if (maildir_storage_sync_force(mbox, uid) < 0)
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen return -1;
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainen }
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainen
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen /* try again */
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen return maildir_uidlist_lookup(mbox->uidlist, uid, flags_r, fname_r);
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainen}
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainen
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainenstatic int maildir_sync_run(struct maildir_mailbox *mbox,
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen enum mailbox_sync_flags flags, bool force_resync,
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen uint32_t *uid, bool *lost_files_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen struct maildir_sync_context *ctx;
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen bool retry, lost_files;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen T_BEGIN {
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen ctx = maildir_sync_context_new(mbox, flags);
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen ret = maildir_sync_context(ctx, force_resync, uid, lost_files_r);
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen retry = ctx->racing;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen maildir_sync_deinit(ctx);
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen } T_END;
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen if (retry) T_BEGIN {
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen /* we're racing some file. retry the sync again to see if the
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen file is really gone or not. if it is, this is a bit of
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen unnecessary work, but if it's not, this is necessary for
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen e.g. doveadm force-resync to work. */
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen ctx = maildir_sync_context_new(mbox, 0);
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen ret = maildir_sync_context(ctx, TRUE, NULL, &lost_files);
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen maildir_sync_deinit(ctx);
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen } T_END;
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen return ret;
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen}
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainenint maildir_storage_sync_force(struct maildir_mailbox *mbox, uint32_t uid)
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen{
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen bool lost_files;
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen int ret;
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen ret = maildir_sync_run(mbox, MAILBOX_SYNC_FLAG_FAST,
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen TRUE, &uid, &lost_files);
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen if (uid != 0) {
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen /* maybe it's expunged. check again. */
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen ret = maildir_sync_run(mbox, 0, TRUE, NULL, &lost_files);
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen }
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen return ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainenint maildir_sync_refresh_flags_view(struct maildir_mailbox *mbox)
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen{
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen struct mail_index_view_sync_ctx *sync_ctx;
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen bool delayed_expunges;
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen mail_index_refresh(mbox->box.index);
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen if (mbox->flags_view == NULL)
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen mbox->flags_view = mail_index_view_open(mbox->box.index);
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen sync_ctx = mail_index_view_sync_begin(mbox->flags_view,
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen MAIL_INDEX_VIEW_SYNC_FLAG_FIX_INCONSISTENT);
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen if (mail_index_view_sync_commit(&sync_ctx, &delayed_expunges) < 0) {
1f1ee8db68d9ae1604350801cd8dc33ebe29fe8aTimo Sirainen mailbox_set_index_error(&mbox->box);
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen return -1;
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen }
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen /* make sure the map stays in private memory */
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen if (mbox->flags_view->map->refcount > 1) {
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen struct mail_index_map *map;
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen map = mail_index_map_clone(mbox->flags_view->map);
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen mail_index_unmap(&mbox->flags_view->map);
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen mbox->flags_view->map = map;
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen }
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen mail_index_record_map_move_to_private(mbox->flags_view->map);
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen mail_index_map_move_to_memory(mbox->flags_view->map);
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen return 0;
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen}
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainenstruct mailbox_sync_context *
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainenmaildir_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
77f8c49beac69a8f5d5d41239528474ee1b877eaJosef 'Jeff' Sipek struct maildir_mailbox *mbox = MAILDIR_MAILBOX(box);
82c70897a2d0e6144ecc56ca8e0eb9fff768f2c5Timo Sirainen bool lost_files, force_resync;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen int ret = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
82c70897a2d0e6144ecc56ca8e0eb9fff768f2c5Timo Sirainen force_resync = (flags & MAILBOX_SYNC_FLAG_FORCE_RESYNC) != 0;
d22301419109ed4a38351715e6760011421dadecTimo Sirainen if (index_mailbox_want_full_sync(&mbox->box, flags)) {
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen ret = maildir_sync_run(mbox, flags, force_resync,
9a107dedb8f35727c21b3d1d54475d33f6e2eb1fTimo Sirainen NULL, &lost_files);
d67ac5f76cc02c227f4997878bb4aef48ee298faTimo Sirainen i_assert(!maildir_uidlist_is_locked(mbox->uidlist) ||
ced118ac5caf6fe83d34339c2c65c63b2aa768acTimo Sirainen (box->flags & MAILBOX_FLAG_KEEP_LOCKED) != 0);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
c2cb5e469cd11759da22d82083d4fbb564d06dfaTimo Sirainen if (lost_files) {
19557f192d37cd54a1a090a8a26d9d47265e4413Aki Tuomi /* lost some files from new/, see if they're in cur/ */
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen ret = maildir_storage_sync_force(mbox, 0);
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
fe813f74aaccb12f38e1bd9cd338c6a37fa646e5Timo Sirainen if (mbox->storage->set->maildir_very_dirty_syncs) {
a8c1d873ebe624cf65893d79e1a509203116cb9aTimo Sirainen if (maildir_sync_refresh_flags_view(mbox) < 0)
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen ret = -1;
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen maildir_uidlist_set_all_nonsynced(mbox->uidlist);
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainen }
3a854fc26bcccb0398f0a9a6fa72db1a4ab8f0b8Timo Sirainen mbox->synced = TRUE;
bfa38f13d605bdd4c6d1f257c46a57bb28c0dd06Timo Sirainen mbox->sync_uidlist_refreshed = FALSE;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen return index_mailbox_sync_init(box, flags, ret < 0);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainenint maildir_sync_is_synced(struct maildir_mailbox *mbox)
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen{
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen bool new_changed, cur_changed;
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen enum maildir_scan_why why;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen int ret;
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen T_BEGIN {
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen const char *box_path = mailbox_get_path(&mbox->box);
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen const char *new_dir, *cur_dir;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen new_dir = t_strconcat(box_path, "/new", NULL);
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen cur_dir = t_strconcat(box_path, "/cur", NULL);
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen
7c449f545b10daa47027552f98d916a9805da662Timo Sirainen ret = maildir_sync_quick_check(mbox, FALSE, new_dir, cur_dir,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen &new_changed, &cur_changed,
ae9365d3de0cefae6f2a5d3e9ab79bc11c37b3d5Timo Sirainen &why);
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen } T_END;
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen return ret < 0 ? -1 : (!new_changed && !cur_changed);
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen}