maildir-sync.c revision 9ef0f38972e7f42ff8d1795f47f6a3e6d621be9c
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2004-2009 Dovecot authors, see the included COPYING file */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen/*
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen Here's a description of how we handle Maildir synchronization and
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen it's problems:
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen We want to be as efficient as we can. The most efficient way to
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen check if changes have occurred is to stat() the new/ and cur/
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen directories and uidlist file - if their mtimes haven't changed,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen there's no changes and we don't need to do anything.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen Problem 1: Multiple changes can happen within a single second -
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen nothing guarantees that once we synced it, someone else didn't just
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen then make a modification. Such modifications wouldn't get noticed
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen until a new modification occurred later.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen Problem 2: Syncing cur/ directory is much more costly than syncing
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen new/. Moving mails from new/ to cur/ will always change mtime of
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen cur/ causing us to sync it as well.
22627da0fb77c1d0d9a8e8bc485ef5540b6f2e69Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen Problem 3: We may not be able to move mail from new/ to cur/
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen because we're out of quota, or simply because we're accessing a
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen read-only mailbox.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen MAILDIR_SYNC_SECS
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen -----------------
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen Several checks below use MAILDIR_SYNC_SECS, which should be maximum
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen clock drift between all computers accessing the maildir (eg. via
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen NFS), rounded up to next second. Our default is 1 second, since
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen everyone should be using NTP.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen Note that setting it to 0 works only if there's only one computer
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen accessing the maildir. It's practically impossible to make two
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen clocks _exactly_ synchronized.
1285518f4f8905f22f5812d022a9f75b51752ed4Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen It might be possible to only use file server's clock by looking at
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen the atime field, but I don't know how well that would actually work.
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen cur directory
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen -------------
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen We have dirty_cur_time variable which is set to cur/ directory's
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mtime when it's >= time() - MAILDIR_SYNC_SECS and we _think_ we have
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen synchronized the directory.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen When dirty_cur_time is non-zero, we don't synchronize the cur/
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen directory until
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen a) cur/'s mtime changes
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen b) opening a mail fails with ENOENT
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen c) time() > dirty_cur_time + MAILDIR_SYNC_SECS
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen This allows us to modify the maildir multiple times without having
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen to sync it at every change. The sync will eventually be done to
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen make sure we didn't miss any external changes.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen The dirty_cur_time is set when:
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen - we change message flags
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen - we expunge messages
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen - we move mail from new/ to cur/
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen - we sync cur/ directory and it's mtime is >= time() - MAILDIR_SYNC_SECS
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen It's unset when we do the final syncing, ie. when mtime is
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen older than time() - MAILDIR_SYNC_SECS.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen new directory
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen -------------
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen If new/'s mtime is >= time() - MAILDIR_SYNC_SECS, always synchronize
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen it. dirty_cur_time-like feature might save us a few syncs, but
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen that might break a client which saves a mail in one connection and
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen tries to fetch it in another one. new/ directory is almost always
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen empty, so syncing it should be very fast anyway. Actually this can
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen still happen if we sync only new/ dir while another client is also
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen moving mails from it to cur/ - it takes us a while to see them.
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen That's pretty unlikely to happen however, and only way to fix it
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen would be to always synchronize cur/ after new/.
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
f4c0b1874b0533bcf2df1d28d584ff02cfdae3faTimo Sirainen Normally we move all mails from new/ to cur/ whenever we sync it. If
f4c0b1874b0533bcf2df1d28d584ff02cfdae3faTimo Sirainen it's not possible for some reason, we mark the mail with "probably
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen exists in new/ directory" flag.
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen If rename() still fails because of ENOSPC or EDQUOT, we still save
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen the flag changes in index with dirty-flag on. When moving the mail
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen to cur/ directory, or when we notice it's already moved there, we
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen apply the flag changes to the filename, rename it and remove the
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen dirty flag. If there's dirty flags, this should be tried every time
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen after expunge or when closing the mailbox.
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen uidlist
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen -------
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen This file contains UID <-> filename mappings. It's updated only when
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen new mail arrives, so it may contain filenames that have already been
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen deleted. Updating is done by getting uidlist.lock file, writing the
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen whole uidlist into it and rename()ing it over the old uidlist. This
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen means there's no need to lock the file for reading.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen Whenever uidlist is rewritten, it's mtime must be larger than the old
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen one's. Use utime() before rename() if needed. Note that inode checking
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen wouldn't have been sufficient as inode numbers can be reused.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen This file is usually read the first time you need to know filename for
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen given UID. After that it's not re-read unless new mails come that we
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen don't know about.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen broken clients
a54fa00087ba926a3d966a8449d8d7579e89911cTimo Sirainen --------------
a54fa00087ba926a3d966a8449d8d7579e89911cTimo Sirainen
a54fa00087ba926a3d966a8449d8d7579e89911cTimo Sirainen Originally the middle identifier in Maildir filename was specified
a54fa00087ba926a3d966a8449d8d7579e89911cTimo Sirainen only as <process id>_<delivery counter>. That however created a
a54fa00087ba926a3d966a8449d8d7579e89911cTimo Sirainen problem with randomized PIDs which made it possible that the same
a54fa00087ba926a3d966a8449d8d7579e89911cTimo Sirainen PID was reused within one second.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen So if within one second a mail was delivered, MUA moved it to cur/
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen and another mail was delivered by a new process using same PID as
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen the first one, we likely ended up overwriting the first mail when
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen the second mail was moved over it.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen Nowadays everyone should be giving a bit more specific identifier,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for example include microseconds in it which Dovecot does.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen There's a simple way to prevent this from happening in some cases:
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen Don't move the mail from new/ to cur/ if it's mtime is >= time() -
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen MAILDIR_SYNC_SECS. The second delivery's link() call then fails
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen because the file is already in new/, and it will then use a
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen different filename. There's a few problems with this however:
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen - it requires extra stat() call which is unneeded extra I/O
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen - another MUA might still move the mail to cur/
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen - if first file's flags are modified by either Dovecot or another
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen MUA, it's moved to cur/ (you _could_ just do the dirty-flagging
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen but that'd be ugly)
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen Because this is useful only for very few people and it requires
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen extra I/O, I decided not to implement this. It should be however
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen quite easy to do since we need to be able to deal with files in new/
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen in any case.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen It's also possible to never accidentally overwrite a mail by using
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen link() + unlink() rather than rename(). This however isn't very
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen good idea as it introduces potential race conditions when multiple
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen clients are accessing the mailbox:
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen Trying to move the same mail from new/ to cur/ at the same time:
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen a) Client 1 uses slightly different filename than client 2,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for example one sets read-flag on but the other doesn't.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen You have the same mail duplicated now.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen b) Client 3 sees the mail between Client 1's and 2's link() calls
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen and changes it's flag. You have the same mail duplicated now.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen And it gets worse when they're unlink()ing in cur/ directory:
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen c) Client 1 changes mails's flag and client 2 changes it back
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen between 1's link() and unlink(). The mail is now expunged.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen d) If you try to deal with the duplicates by unlink()ing another
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen one of them, you might end up unlinking both of them.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen So, what should we do then if we notice a duplicate? First of all,
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen it might not be a duplicate at all, readdir() might have just
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen returned it twice because it was just renamed. What we should do is
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen create a completely new base name for it and rename() it to that.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen If the call fails with ENOENT, it only means that it wasn't a
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen duplicate after all.
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen*/
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen#include "lib.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "ioloop.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "array.h"
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen#include "buffer.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "hash.h"
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen#include "str.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "nfs-workarounds.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "maildir-storage.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "maildir-uidlist.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "maildir-filename.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "maildir-sync.h"
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include <stdio.h>
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include <stddef.h>
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include <stdlib.h>
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include <unistd.h>
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include <dirent.h>
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include <sys/stat.h>
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#define MAILDIR_FILENAME_FLAG_FOUND 128
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen/* When rename()ing many files from new/ to cur/, it's possible that next
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen readdir() skips some files. we don't of course wish to lose them, so we
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen go and rescan the new/ directory again from beginning until no files are
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen left. This value is just an optimization to avoid checking the directory
e9a0dc3e1e63f0f3affbcea7a0f5ffc112f34427Timo Sirainen twice unneededly. usually only NFS is the problem case. 1 is the safest
e9a0dc3e1e63f0f3affbcea7a0f5ffc112f34427Timo Sirainen bet here, but I guess 5 will do just fine too. */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#define MAILDIR_RENAME_RESCAN_COUNT 5
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen/* This is mostly to avoid infinite looping when rename() destination already
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen exists as the hard link of the file itself. */
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen#define MAILDIR_SCAN_DIR_MAX_COUNT 5
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#define DUPE_LINKS_DELETE_SECS 30
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstruct maildir_sync_context {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct maildir_mailbox *mbox;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *new_dir, *cur_dir;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
944a12ae4f453cc3f8a25f1e9047a5094fdfe828Timo Sirainen enum mailbox_sync_flags flags;
944a12ae4f453cc3f8a25f1e9047a5094fdfe828Timo Sirainen time_t last_touch, last_notify;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct maildir_uidlist_sync_ctx *uidlist_sync_ctx;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct maildir_index_sync_context *index_sync_ctx;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int partial:1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int locked:1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen};
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenvoid maildir_sync_notify(struct maildir_sync_context *ctx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen time_t now;
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen if (ctx == NULL) {
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen /* we got here from maildir-save.c. it has no
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen maildir_sync_context, */
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen return;
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen }
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen now = time(NULL);
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen if (now - ctx->last_touch > MAILDIR_LOCK_TOUCH_SECS && ctx->locked) {
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen (void)maildir_uidlist_lock_touch(ctx->mbox->uidlist);
5fe06fea9fee0f5e4e9cb49f6866877223f78b85Timo Sirainen ctx->last_touch = now;
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (now - ctx->last_notify > MAIL_STORAGE_STAYALIVE_SECS) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct mailbox *box = &ctx->mbox->ibox.box;
22627da0fb77c1d0d9a8e8bc485ef5540b6f2e69Timo Sirainen
3e28b527dd6048a40684afd29cff0ee008fc0014Timo Sirainen if (box->storage->callbacks->notify_ok != NULL) {
3e28b527dd6048a40684afd29cff0ee008fc0014Timo Sirainen box->storage->callbacks->
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen notify_ok(box, "Hang in there..",
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen box->storage->callback_context);
ccffb125d94adff0ad776de5a96e22f864d6fb0aTimo Sirainen }
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen ctx->last_notify = now;
ccffb125d94adff0ad776de5a96e22f864d6fb0aTimo Sirainen }
ccffb125d94adff0ad776de5a96e22f864d6fb0aTimo Sirainen}
ccffb125d94adff0ad776de5a96e22f864d6fb0aTimo Sirainen
ccffb125d94adff0ad776de5a96e22f864d6fb0aTimo Sirainenstatic struct maildir_sync_context *
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainenmaildir_sync_context_new(struct maildir_mailbox *mbox,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen enum mailbox_sync_flags flags)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
b221779c191d1fb5fa7eb03907e62d39d1edeb08Timo Sirainen struct maildir_sync_context *ctx;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx = t_new(struct maildir_sync_context, 1);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->mbox = mbox;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->new_dir = t_strconcat(mbox->path, "/new", NULL);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->cur_dir = t_strconcat(mbox->path, "/cur", NULL);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->last_touch = ioloop_time;
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen ctx->last_notify = ioloop_time;
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen ctx->flags = flags;
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen return ctx;
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void maildir_sync_deinit(struct maildir_sync_context *ctx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (ctx->uidlist_sync_ctx != NULL)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen (void)maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (ctx->index_sync_ctx != NULL) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen (void)maildir_sync_index_finish(&ctx->index_sync_ctx,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen TRUE, FALSE);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen }
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic int maildir_fix_duplicate(struct maildir_sync_context *ctx,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen const char *dir, const char *fname2)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
a1aaf11831cab8346d6d0dc702e37b3f1d95eb43Timo Sirainen const char *fname1, *path1, *path2;
a1aaf11831cab8346d6d0dc702e37b3f1d95eb43Timo Sirainen const char *new_fname, *new_path;
a1aaf11831cab8346d6d0dc702e37b3f1d95eb43Timo Sirainen struct stat st1, st2;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen fname1 = maildir_uidlist_sync_get_full_filename(ctx->uidlist_sync_ctx,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen fname2);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_assert(fname1 != NULL);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen path1 = t_strconcat(dir, "/", fname1, NULL);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen path2 = t_strconcat(dir, "/", fname2, NULL);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (stat(path1, &st1) < 0 || stat(path2, &st2) < 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* most likely the files just don't exist anymore.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen don't really care about other errors much. */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen if (st1.st_ino == st2.st_ino &&
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen CMP_DEV_T(st1.st_dev, st2.st_dev)) {
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen /* Files are the same. this means either a race condition
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen between stat() calls, or that the files were link()ed. */
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen if (st1.st_nlink > 1 && st2.st_nlink == st1.st_nlink &&
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen st1.st_ctime == st2.st_ctime &&
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen st1.st_ctime < ioloop_time - DUPE_LINKS_DELETE_SECS) {
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen /* The file has hard links and it hasn't had any
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen changes (such as renames) for a while, so this
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen isn't a race condition.
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen rename()ing one file on top of the other would fix
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen this safely, except POSIX decided that rename()
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen doesn't work that way. So we'll have unlink() one
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen and hope that another process didn't just decide to
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unlink() the other (uidlist lock prevents this from
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen happening) */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (unlink(path2) == 0)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen i_warning("Unlinked a duplicate: %s", path2);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen else {
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen mail_storage_set_critical(
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen &ctx->mbox->storage->storage,
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen "unlink(%s) failed: %m", path2);
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen }
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen }
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen return 0;
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen }
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen new_fname = maildir_filename_generate();
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen new_path = t_strconcat(ctx->mbox->path, "/new/", new_fname, NULL);
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen if (rename(path2, new_path) == 0)
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen i_warning("Fixed a duplicate: %s -> %s", path2, new_fname);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen else if (errno != ENOENT) {
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen mail_storage_set_critical(&ctx->mbox->storage->storage,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen "Couldn't fix a duplicate: rename(%s, %s) failed: %m",
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen path2, new_path);
46744f1c9837f189e1c1b13e4d83231b3c9dfff6Timo Sirainen return -1;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen }
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen return 0;
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen}
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen
b221779c191d1fb5fa7eb03907e62d39d1edeb08Timo Sirainenstatic int
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainenmaildir_stat(struct maildir_mailbox *mbox, const char *path, struct stat *st_r)
b221779c191d1fb5fa7eb03907e62d39d1edeb08Timo Sirainen{
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen int i;
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen for (i = 0;; i++) {
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen if (nfs_safe_stat(path, st_r) == 0)
b221779c191d1fb5fa7eb03907e62d39d1edeb08Timo Sirainen return 0;
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT)
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen break;
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen if (!maildir_set_deleted(mbox))
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen return -1;
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen /* try again */
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen }
b221779c191d1fb5fa7eb03907e62d39d1edeb08Timo Sirainen
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen mail_storage_set_critical(mbox->ibox.box.storage,
3656c91dcb8336814bebd4500e81c3dde25233e6Timo Sirainen "stat(%s) failed: %m", path);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen return -1;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen}
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic int maildir_scan_dir(struct maildir_sync_context *ctx, bool new_dir)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mail_storage *storage = &ctx->mbox->storage->storage;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *path;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen DIR *dirp;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen string_t *src, *dest;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct dirent *dp;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct stat st;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen enum maildir_uidlist_rec_flag flags;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int i = 0, move_count = 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen time_t now;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen int ret = 1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen bool move_new, check_touch, dir_changed = FALSE;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen path = new_dir ? ctx->new_dir : ctx->cur_dir;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for (i = 0;; i++) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen dirp = opendir(path);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (dirp != NULL)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen break;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_storage_set_critical(storage,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen "opendir(%s) failed: %m", path);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (!maildir_set_deleted(ctx->mbox))
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* try again */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#ifdef HAVE_DIRFD
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (fstat(dirfd(dirp), &st) < 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_storage_set_critical(storage,
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen "fstat(%s) failed: %m", path);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen (void)closedir(dirp);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#else
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (maildir_stat(ctx->mbox, path, &st) < 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen (void)closedir(dirp);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#endif
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen now = time(NULL);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (new_dir) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->mbox->maildir_hdr.new_check_time = now;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->mbox->maildir_hdr.new_mtime = st.st_mtime;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->mbox->maildir_hdr.new_mtime_nsecs = ST_MTIME_NSEC(st);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen } else {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->mbox->maildir_hdr.cur_check_time = now;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->mbox->maildir_hdr.cur_mtime = st.st_mtime;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->mbox->maildir_hdr.cur_mtime_nsecs = ST_MTIME_NSEC(st);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen src = t_str_new(1024);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen dest = t_str_new(1024);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen move_new = new_dir && !mailbox_is_readonly(&ctx->mbox->ibox.box) &&
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen !ctx->mbox->ibox.keep_recent && ctx->locked;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen errno = 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for (; (dp = readdir(dirp)) != NULL; errno = 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (dp->d_name[0] == '.')
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen continue;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen check_touch = FALSE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen flags = 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (move_new) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen str_truncate(src, 0);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen str_truncate(dest, 0);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen str_printfa(src, "%s/%s", ctx->new_dir, dp->d_name);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen str_printfa(dest, "%s/%s", ctx->cur_dir, dp->d_name);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (strchr(dp->d_name, MAILDIR_INFO_SEP) == NULL) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen str_append(dest, MAILDIR_FLAGS_FULL_SEP);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (rename(str_c(src), str_c(dest)) == 0) {
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen /* we moved it - it's \Recent for us */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen dir_changed = TRUE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen move_count++;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen flags |= MAILDIR_UIDLIST_REC_FLAG_MOVED |
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen MAILDIR_UIDLIST_REC_FLAG_RECENT;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen } else if (ENOTFOUND(errno)) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* someone else moved it already */
dd171dccbe98fc63ca737e6e4e8edbeb601e5cbdTimo Sirainen dir_changed = TRUE;
dd171dccbe98fc63ca737e6e4e8edbeb601e5cbdTimo Sirainen move_count++;
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen flags |= MAILDIR_UIDLIST_REC_FLAG_MOVED |
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen MAILDIR_UIDLIST_REC_FLAG_RECENT;
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen } else if (ENOSPACE(errno) || errno == EACCES) {
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen /* not enough disk space / read-only maildir,
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen leave here */
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
dd171dccbe98fc63ca737e6e4e8edbeb601e5cbdTimo Sirainen move_new = FALSE;
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen } else {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_storage_set_critical(storage,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen "rename(%s, %s) failed: %m",
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen str_c(src), str_c(dest));
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if ((move_count % MAILDIR_SLOW_MOVE_COUNT) == 0)
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen maildir_sync_notify(ctx);
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen } else if (new_dir) {
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen MAILDIR_UIDLIST_REC_FLAG_RECENT;
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen }
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen i++;
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen if ((i % MAILDIR_SLOW_CHECK_COUNT) == 0)
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen maildir_sync_notify(ctx);
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen ret = maildir_uidlist_sync_next(ctx->uidlist_sync_ctx,
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen dp->d_name, flags);
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen if (ret <= 0) {
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen if (ret < 0)
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen break;
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen /* possibly duplicate - try fixing it */
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen T_BEGIN {
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen ret = maildir_fix_duplicate(ctx, path,
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen dp->d_name);
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen } T_END;
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen if (ret < 0)
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen break;
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen }
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (errno != 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_storage_set_critical(storage,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen "readdir(%s) failed: %m", path);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ret = -1;
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen }
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (closedir(dirp) < 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_storage_set_critical(storage,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen "closedir(%s) failed: %m", path);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ret = -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (dir_changed) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (new_dir)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->mbox->maildir_hdr.new_mtime = now;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen else
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->mbox->maildir_hdr.cur_mtime = now;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return ret < 0 ? -1 :
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen (move_count <= MAILDIR_RENAME_RESCAN_COUNT ? 0 : 1);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenint maildir_sync_header_refresh(struct maildir_mailbox *mbox)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const void *data;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen size_t data_size;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
dd171dccbe98fc63ca737e6e4e8edbeb601e5cbdTimo Sirainen if (mail_index_refresh(mbox->ibox.index) < 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_storage_set_index_error(&mbox->ibox);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_index_get_header_ext(mbox->ibox.view, mbox->maildir_ext_id,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen &data, &data_size);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (data_size == 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* doesn't exist */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return 0;
a1aaf11831cab8346d6d0dc702e37b3f1d95eb43Timo Sirainen }
a1aaf11831cab8346d6d0dc702e37b3f1d95eb43Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen memcpy(&mbox->maildir_hdr, data,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen I_MIN(sizeof(mbox->maildir_hdr), data_size));
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic int maildir_sync_quick_check(struct maildir_mailbox *mbox, bool undirty,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *new_dir, const char *cur_dir,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen bool *new_changed_r, bool *cur_changed_r)
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#define DIR_DELAYED_REFRESH(hdr, name) \
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ((hdr)->name ## _check_time <= \
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen (hdr)->name ## _mtime + MAILDIR_SYNC_SECS && \
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen (undirty || \
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen (time_t)(hdr)->name ## _check_time < ioloop_time - MAILDIR_SYNC_SECS))
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#define DIR_MTIME_CHANGED(st, hdr, name) \
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ((st).st_mtime != (time_t)(hdr)->name ## _mtime || \
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen !ST_NTIMES_EQUAL(ST_MTIME_NSEC(st), (hdr)->name ## _mtime_nsecs))
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct maildir_index_header *hdr = &mbox->maildir_hdr;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct stat new_st, cur_st;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen bool refreshed = FALSE, check_new = FALSE, check_cur = FALSE;
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen if (mbox->maildir_hdr.new_mtime == 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (maildir_sync_header_refresh(mbox) < 0)
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (mbox->maildir_hdr.new_mtime == 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* first sync */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen *new_changed_r = *cur_changed_r = TRUE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return 0;
13a8c553f293349248b161ff851743498916e26eTimo Sirainen }
13a8c553f293349248b161ff851743498916e26eTimo Sirainen }
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen *new_changed_r = *cur_changed_r = FALSE;
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen /* try to avoid stat()ing by first checking delayed changes */
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen if (DIR_DELAYED_REFRESH(hdr, new) ||
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen DIR_DELAYED_REFRESH(hdr, cur)) {
9fcf7b79236b0045f7709718f7b65ada516565e7Timo Sirainen /* refresh index and try again */
9fcf7b79236b0045f7709718f7b65ada516565e7Timo Sirainen if (maildir_sync_header_refresh(mbox) < 0)
9fcf7b79236b0045f7709718f7b65ada516565e7Timo Sirainen return -1;
9fcf7b79236b0045f7709718f7b65ada516565e7Timo Sirainen refreshed = TRUE;
c9343c25215e98880db8f9e9c5f120f6311bc06dTimo Sirainen
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen if (DIR_DELAYED_REFRESH(hdr, new))
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen *new_changed_r = TRUE;
c9343c25215e98880db8f9e9c5f120f6311bc06dTimo Sirainen if (DIR_DELAYED_REFRESH(hdr, cur))
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen *cur_changed_r = TRUE;
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen if (*new_changed_r && *cur_changed_r)
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen return 0;
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen }
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen if (!*new_changed_r) {
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen if (maildir_stat(mbox, new_dir, &new_st) < 0)
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen check_new = TRUE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (!*cur_changed_r) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (maildir_stat(mbox, cur_dir, &cur_st) < 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen check_cur = TRUE;
3656c91dcb8336814bebd4500e81c3dde25233e6Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen for (;;) {
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen if (check_new)
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen *new_changed_r = DIR_MTIME_CHANGED(new_st, hdr, new);
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen if (check_cur)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen *cur_changed_r = DIR_MTIME_CHANGED(cur_st, hdr, cur);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if ((!*new_changed_r && !*cur_changed_r) || refreshed)
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen break;
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen /* refresh index and try again */
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen if (maildir_sync_header_refresh(mbox) < 0)
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen return -1;
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen refreshed = TRUE;
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen }
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen return 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void maildir_sync_update_next_uid(struct maildir_mailbox *mbox)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const struct mail_index_header *hdr;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen uint32_t uid_validity, next_uid;
3656c91dcb8336814bebd4500e81c3dde25233e6Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen hdr = mail_index_get_header(mbox->ibox.view);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (hdr->uid_validity == 0)
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen return;
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen uid_validity = maildir_uidlist_get_uid_validity(mbox->uidlist);
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen next_uid = maildir_uidlist_get_next_uid(mbox->uidlist);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (uid_validity == hdr->uid_validity || uid_validity == 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* make sure uidlist's next_uid is at least as large as
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen index file's. typically this happens only if uidlist gets
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen deleted. */
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen maildir_uidlist_set_uid_validity(mbox->uidlist,
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen hdr->uid_validity);
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen maildir_uidlist_set_next_uid(mbox->uidlist,
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen hdr->next_uid, FALSE);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen }
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic bool move_recent_messages(struct maildir_sync_context *ctx)
1285518f4f8905f22f5812d022a9f75b51752ed4Timo Sirainen{
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen const struct mail_index_header *hdr;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (ctx->mbox->ibox.keep_recent)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen return FALSE;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen (void)maildir_uidlist_refresh(ctx->mbox->uidlist);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen /* if there are files in new/, we'll need to move them. we'll check
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen this by checking if we have any recent messages */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen hdr = mail_index_get_header(ctx->mbox->ibox.view);
5fe06fea9fee0f5e4e9cb49f6866877223f78b85Timo Sirainen return hdr->first_recent_uid <
5fe06fea9fee0f5e4e9cb49f6866877223f78b85Timo Sirainen maildir_uidlist_get_next_uid(ctx->mbox->uidlist);
5fe06fea9fee0f5e4e9cb49f6866877223f78b85Timo Sirainen}
5fe06fea9fee0f5e4e9cb49f6866877223f78b85Timo Sirainen
5fe06fea9fee0f5e4e9cb49f6866877223f78b85Timo Sirainenstatic int maildir_sync_get_changes(struct maildir_sync_context *ctx,
5fe06fea9fee0f5e4e9cb49f6866877223f78b85Timo Sirainen bool *new_changed_r, bool *cur_changed_r)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen{
510a871e2187891d538bf2ebb3cfd2056003af88Timo Sirainen enum mail_index_sync_flags flags = 0;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen bool undirty = (ctx->flags & MAILBOX_SYNC_FLAG_FULL_READ) != 0;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (maildir_sync_quick_check(ctx->mbox, undirty,
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen ctx->new_dir, ctx->cur_dir,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen new_changed_r, cur_changed_r) < 0)
510a871e2187891d538bf2ebb3cfd2056003af88Timo Sirainen return -1;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
510a871e2187891d538bf2ebb3cfd2056003af88Timo Sirainen if (*new_changed_r || *cur_changed_r)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen return 1;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (move_recent_messages(ctx)) {
1285518f4f8905f22f5812d022a9f75b51752ed4Timo Sirainen *new_changed_r = TRUE;
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen return 1;
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen }
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen if (!ctx->mbox->ibox.keep_recent)
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen flags |= MAIL_INDEX_SYNC_FLAG_DROP_RECENT;
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen
840a3701b7a0f7fadd17738998c33790a8dfad2dTimo Sirainen return mail_index_sync_have_any(ctx->mbox->ibox.index, flags) ? 1 : 0;
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen}
840a3701b7a0f7fadd17738998c33790a8dfad2dTimo Sirainen
840a3701b7a0f7fadd17738998c33790a8dfad2dTimo Sirainenstatic int maildir_sync_context(struct maildir_sync_context *ctx, bool forced,
840a3701b7a0f7fadd17738998c33790a8dfad2dTimo Sirainen uint32_t *find_uid, bool *lost_files_r)
840a3701b7a0f7fadd17738998c33790a8dfad2dTimo Sirainen{
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen enum maildir_uidlist_sync_flags sync_flags;
840a3701b7a0f7fadd17738998c33790a8dfad2dTimo Sirainen enum maildir_uidlist_rec_flag flags;
840a3701b7a0f7fadd17738998c33790a8dfad2dTimo Sirainen bool new_changed, cur_changed, lock_failure;
5fe06fea9fee0f5e4e9cb49f6866877223f78b85Timo Sirainen int ret;
5fe06fea9fee0f5e4e9cb49f6866877223f78b85Timo Sirainen
5fe06fea9fee0f5e4e9cb49f6866877223f78b85Timo Sirainen *lost_files_r = FALSE;
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (forced)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen new_changed = cur_changed = TRUE;
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen else {
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen ret = maildir_sync_get_changes(ctx, &new_changed, &cur_changed);
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen if (ret <= 0)
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen return ret;
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen }
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /*
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen Locking, locking, locking.. Wasn't maildir supposed to be lockless?
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen We can get here either as beginning a real maildir sync, or when
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen committing changes to maildir but a file was lost (maybe renamed).
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen So, we're going to need two locks. One for index and one for
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen uidlist. To avoid deadlocking do the uidlist lock always first.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen uidlist is needed only for figuring out UIDs for newly seen files,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen so theoretically we wouldn't need to lock it unless there are new
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen files. It has a few problems though, assuming the index lock didn't
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen already protect it (eg. in-memory indexes):
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen 1. Just because you see a new file which doesn't exist in uidlist
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen file, doesn't mean that the file really exists anymore, or that
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen your readdir() lists all new files. Meaning that this is possible:
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen A: opendir(), readdir() -> new file ...
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen -- new files are written to the maildir --
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen B: opendir(), readdir() -> new file, lock uidlist,
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen readdir() -> another new file, rewrite uidlist, unlock
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen A: ... lock uidlist, readdir() -> nothing left, rewrite uidlist,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unlock
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen The second time running A didn't see the two new files. To handle
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen this correctly, it must not remove the new unseen files from
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen uidlist. This is possible to do, but adds extra complexity.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen 2. If another process is rename()ing files while we are
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen readdir()ing, it's possible that readdir() never lists some files,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen causing Dovecot to assume they were expunged. In next sync they
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen would show up again, but client could have already been notified of
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen that and they would show up under new UIDs, so the damage is
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen already done.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen Both of the problems can be avoided if we simply lock the uidlist
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen before syncing and keep it until sync is finished. Typically this
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen would happen in any case, as there is the index lock..
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen The second case is still a problem with external changes though,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen because maildir doesn't require any kind of locking. Luckily this
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen problem rarely happens except under high amount of modifications.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (!cur_changed) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->partial = TRUE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen sync_flags = MAILDIR_UIDLIST_SYNC_PARTIAL;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen } else {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->partial = FALSE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen sync_flags = 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (forced)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen sync_flags |= MAILDIR_UIDLIST_SYNC_FORCE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if ((ctx->flags & MAILBOX_SYNC_FLAG_FAST) != 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen sync_flags |= MAILDIR_UIDLIST_SYNC_TRYLOCK;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ret = maildir_uidlist_sync_init(ctx->mbox->uidlist, sync_flags,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen &ctx->uidlist_sync_ctx);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen lock_failure = ret <= 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (ret <= 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mail_storage *storage = ctx->mbox->ibox.box.storage;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (ret == 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* timeout */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* locking failed. sync anyway without locking so that it's
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen possible to expunge messages when out of quota. */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (forced) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* we're already forcing a sync, we're trying to find
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen a message that was probably already expunged, don't
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen loop for a long time trying to find it. */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ret = maildir_uidlist_sync_init(ctx->mbox->uidlist, sync_flags |
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen MAILDIR_UIDLIST_SYNC_NOLOCK,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen &ctx->uidlist_sync_ctx);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_assert(ret > 0);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (storage->callbacks->notify_no != NULL) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen storage->callbacks->notify_no(&ctx->mbox->ibox.box,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen "Internal mailbox synchronization failure, "
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen "showing only old mails.",
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen storage->callback_context);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->locked = maildir_uidlist_is_locked(ctx->mbox->uidlist);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (!ctx->locked)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->partial = TRUE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (!ctx->mbox->syncing_commit && (ctx->locked || lock_failure)) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (maildir_sync_index_begin(ctx->mbox, ctx,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen &ctx->index_sync_ctx) < 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (new_changed || cur_changed) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* if we're going to check cur/ dir our current logic requires
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen that new/ dir is checked as well. it's a good idea anyway. */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int count = 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen while ((ret = maildir_scan_dir(ctx, TRUE)) > 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* rename()d at least some files, which might have
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen caused some other files to be missed. check again
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen (see MAILDIR_RENAME_RESCAN_COUNT). */
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen if (++count > MAILDIR_SCAN_DIR_MAX_COUNT)
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen break;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen }
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen if (ret < 0)
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen return -1;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen if (cur_changed) {
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen if (maildir_scan_dir(ctx, FALSE) < 0)
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen return -1;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen }
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen maildir_sync_update_next_uid(ctx->mbox);
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen /* finish uidlist syncing, but keep it still locked */
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen maildir_uidlist_sync_finish(ctx->uidlist_sync_ctx);
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen }
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen if (!ctx->locked) {
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen /* make sure we sync the maildir later */
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen ctx->mbox->maildir_hdr.new_mtime = 0;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen ctx->mbox->maildir_hdr.cur_mtime = 0;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen }
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen if (ctx->index_sync_ctx != NULL) {
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen /* NOTE: index syncing here might cause a re-sync due to
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen files getting lost, so this function might be called
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen re-entrantly. */
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen ret = maildir_sync_index(ctx->index_sync_ctx, ctx->partial);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (maildir_sync_index_finish(&ctx->index_sync_ctx,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ret < 0, FALSE) < 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (ret < 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (ret == 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen *lost_files_r = TRUE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_assert(maildir_uidlist_is_locked(ctx->mbox->uidlist) ||
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen lock_failure);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (find_uid != NULL && *find_uid != 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (maildir_uidlist_lookup_nosync(ctx->mbox->uidlist, *find_uid,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen &flags) == NULL) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* UID is expunged */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen *find_uid = 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen } else if ((flags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) == 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* we didn't find it, possibly expunged? */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen *find_uid = 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenint maildir_storage_sync_force(struct maildir_mailbox *mbox, uint32_t uid)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct maildir_sync_context *ctx;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen bool lost_files;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen int ret;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen T_BEGIN {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx = maildir_sync_context_new(mbox, MAILBOX_SYNC_FLAG_FAST);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen ret = maildir_sync_context(ctx, TRUE, &uid, &lost_files);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen maildir_sync_deinit(ctx);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen } T_END;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen if (uid != 0) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* maybe it's expunged. check again. */
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen T_BEGIN {
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen ctx = maildir_sync_context_new(mbox, 0);
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen ret = maildir_sync_context(ctx, TRUE, NULL,
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen &lost_files);
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen maildir_sync_deinit(ctx);
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen } T_END;
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen }
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen return ret;
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen}
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainenstruct mailbox_sync_context *
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainenmaildir_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen{
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct maildir_sync_context *ctx;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen bool lost_files;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen int ret = 0;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (!box->opened)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen index_storage_mailbox_open(&mbox->ibox);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (index_mailbox_want_full_sync(&mbox->ibox, flags)) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen T_BEGIN {
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen ctx = maildir_sync_context_new(mbox, flags);
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen ret = maildir_sync_context(ctx, FALSE, NULL,
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen &lost_files);
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen maildir_sync_deinit(ctx);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen } T_END;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen i_assert(!maildir_uidlist_is_locked(mbox->uidlist) ||
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen mbox->ibox.keep_locked);
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen if (lost_files) {
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen /* lost some files from new/, see if thery're in cur/ */
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen ret = maildir_storage_sync_force(mbox, 0);
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen }
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen }
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen return index_mailbox_sync_init(box, flags, ret < 0);
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen}
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenint maildir_sync_is_synced(struct maildir_mailbox *mbox)
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen{
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen bool new_changed, cur_changed;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen int ret;
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen T_BEGIN {
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen const char *new_dir, *cur_dir;
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen new_dir = t_strconcat(mbox->path, "/new", NULL);
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen cur_dir = t_strconcat(mbox->path, "/cur", NULL);
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen ret = maildir_sync_quick_check(mbox, FALSE, new_dir, cur_dir,
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen &new_changed, &cur_changed);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen } T_END;
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen return ret < 0 ? -1 : (!new_changed && !cur_changed);
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen}
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen