maildir-sync.c revision 5fb3bff645380804c9db2510940c41db6b8fdb01
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen/* Copyright (C) 2004 Timo Sirainen */
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"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "maildir-storage.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "maildir-uidlist.h"
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen#include "maildir-keywords.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
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen twice unneededly. 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
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct maildir_sync_context {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen struct maildir_mailbox *mbox;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *new_dir, *cur_dir;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen bool partial;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen struct maildir_uidlist_sync_ctx *uidlist_sync_ctx;
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen struct maildir_index_sync_context *index_sync_ctx;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen};
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainenstruct maildir_index_sync_context {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen struct maildir_mailbox *mbox;
b35f7104715edee0cfac6d46ab0b342033867eb7Timo Sirainen struct mail_index_view *view;
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen struct mail_index_sync_ctx *sync_ctx;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct maildir_keywords_sync_ctx *keywords_sync_ctx;
b35f7104715edee0cfac6d46ab0b342033867eb7Timo Sirainen struct mail_index_transaction *trans;
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen ARRAY_DEFINE(sync_recs, struct mail_index_sync_rec);
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen uint32_t seq;
b92813e2f96d4b28f989528ed5dd6115da7d9bdbTimo Sirainen int dirty_state;
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen};
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainenstruct maildir_keywords_sync_ctx *
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainenmaildir_sync_get_keywords_sync_ctx(struct maildir_index_sync_context *ctx)
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen{
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen return ctx->keywords_sync_ctx;
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen}
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainenint maildir_filename_get_flags(struct maildir_keywords_sync_ctx *ctx,
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen const char *fname, enum mail_flags *flags_r,
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen ARRAY_TYPE(keyword_indexes) *keywords_r)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen{
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen const char *info;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen array_clear(keywords_r);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *flags_r = 0;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen info = strchr(fname, MAILDIR_INFO_SEP);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (info == NULL || info[1] != '2' || info[2] != MAILDIR_FLAGS_SEP)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return 0;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen for (info += 3; *info != '\0' && *info != MAILDIR_FLAGS_SEP; info++) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen switch (*info) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen case 'R': /* replied */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *flags_r |= MAIL_ANSWERED;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen break;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen case 'S': /* seen */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *flags_r |= MAIL_SEEN;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen break;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen case 'T': /* trashed */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *flags_r |= MAIL_DELETED;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen break;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen case 'D': /* draft */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *flags_r |= MAIL_DRAFT;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen break;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen case 'F': /* flagged */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *flags_r |= MAIL_FLAGGED;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen break;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen default:
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (*info >= MAILDIR_KEYWORD_FIRST &&
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *info <= MAILDIR_KEYWORD_LAST) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen int idx;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen idx = maildir_keywords_char_idx(ctx, *info);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (idx < 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* unknown keyword. */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen break;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
0db4290d60bfa00774f628276d38654c56abd68cTimo Sirainen array_append(keywords_r,
0db4290d60bfa00774f628276d38654c56abd68cTimo Sirainen (unsigned int *)&idx, 1);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen break;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* unknown flag - ignore */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen break;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return 1;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen}
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenstatic void
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenmaildir_filename_append_keywords(struct maildir_keywords_sync_ctx *ctx,
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen ARRAY_TYPE(keyword_indexes) *keywords,
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen string_t *str)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen{
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen const unsigned int *indexes;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen unsigned int i, count;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen char chr;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen indexes = array_get(keywords, &count);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen for (i = 0; i < count; i++) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen chr = maildir_keywords_idx_char(ctx, indexes[i]);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (chr != '\0')
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen str_append_c(str, chr);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen}
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainenconst char *maildir_filename_set_flags(struct maildir_keywords_sync_ctx *ctx,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen const char *fname, enum mail_flags flags,
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen ARRAY_TYPE(keyword_indexes) *keywords)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen{
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen string_t *flags_str;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen enum mail_flags flags_left;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen const char *info, *oldflags;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen int nextflag;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* remove the old :info from file name, and get the old flags */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen info = strrchr(fname, MAILDIR_INFO_SEP);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (info != NULL && strrchr(fname, '/') > info)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen info = NULL;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen oldflags = "";
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (info != NULL) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen fname = t_strdup_until(fname, info);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (info[1] == '2' && info[2] == MAILDIR_FLAGS_SEP)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen oldflags = info+3;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* insert the new flags between old flags. flags must be sorted by
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen their ASCII code. unknown flags are kept. */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen flags_str = t_str_new(256);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen str_append(flags_str, fname);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen str_append(flags_str, MAILDIR_FLAGS_FULL_SEP);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen flags_left = flags;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen for (;;) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* skip all known flags */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen while (*oldflags == 'D' || *oldflags == 'F' ||
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *oldflags == 'R' || *oldflags == 'S' ||
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *oldflags == 'T' ||
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen (*oldflags >= MAILDIR_KEYWORD_FIRST &&
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen *oldflags <= MAILDIR_KEYWORD_LAST))
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen oldflags++;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen nextflag = *oldflags == '\0' || *oldflags == MAILDIR_FLAGS_SEP ?
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen 256 : (unsigned char) *oldflags;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if ((flags_left & MAIL_DRAFT) && nextflag > 'D') {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen str_append_c(flags_str, 'D');
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen flags_left &= ~MAIL_DRAFT;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if ((flags_left & MAIL_FLAGGED) && nextflag > 'F') {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen str_append_c(flags_str, 'F');
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen flags_left &= ~MAIL_FLAGGED;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if ((flags_left & MAIL_ANSWERED) && nextflag > 'R') {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen str_append_c(flags_str, 'R');
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen flags_left &= ~MAIL_ANSWERED;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if ((flags_left & MAIL_SEEN) && nextflag > 'S') {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen str_append_c(flags_str, 'S');
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen flags_left &= ~MAIL_SEEN;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if ((flags_left & MAIL_DELETED) && nextflag > 'T') {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen str_append_c(flags_str, 'T');
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen flags_left &= ~MAIL_DELETED;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (keywords != NULL && array_is_created(keywords) &&
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen nextflag > MAILDIR_KEYWORD_FIRST) {
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen maildir_filename_append_keywords(ctx, keywords,
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen flags_str);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen keywords = NULL;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (*oldflags == '\0' || *oldflags == MAILDIR_FLAGS_SEP)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen break;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen str_append_c(flags_str, *oldflags);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen oldflags++;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (*oldflags == MAILDIR_FLAGS_SEP) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* another flagset, we don't know about these, just keep them */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen while (*oldflags != '\0')
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen str_append_c(flags_str, *oldflags++);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return str_c(flags_str);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen}
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenstatic int maildir_expunge(struct maildir_mailbox *mbox, const char *path,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen void *context __attr_unused__)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (unlink(path) == 0) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->dirty_cur_time = ioloop_time;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 1;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (errno == ENOENT)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
8153fdec343e40e2a78f5c12353e89b994b28f74Timo Sirainen mail_storage_set_critical(STORAGE(mbox->storage),
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "unlink(%s) failed: %m", path);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenstatic int maildir_sync_flags(struct maildir_mailbox *mbox, const char *path,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen void *context)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen struct maildir_index_sync_context *ctx = context;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen const struct mail_index_sync_rec *recs;
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen const char *dir, *fname, *newfname, *newpath;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen enum mail_flags flags;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen ARRAY_TYPE(keyword_indexes) keywords;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen unsigned int i, count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uint8_t flags8;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
b92813e2f96d4b28f989528ed5dd6115da7d9bdbTimo Sirainen ctx->dirty_state = 0;
b35f7104715edee0cfac6d46ab0b342033867eb7Timo Sirainen
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen fname = strrchr(path, '/');
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen i_assert(fname != NULL);
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen fname++;
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen dir = t_strdup_until(path, fname);
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen t_array_init(&keywords, 16);
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen (void)maildir_filename_get_flags(ctx->keywords_sync_ctx,
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen fname, &flags, &keywords);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen flags8 = flags;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen recs = array_get_modifiable(&ctx->sync_recs, &count);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen for (i = 0; i < count; i++) {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (recs[i].uid1 != ctx->seq)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen break;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen switch (recs[i].type) {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen case MAIL_INDEX_SYNC_TYPE_FLAGS:
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen mail_index_sync_flags_apply(&recs[i], &flags8);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen break;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD:
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE:
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen case MAIL_INDEX_SYNC_TYPE_KEYWORD_RESET:
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen mail_index_sync_keywords_apply(&recs[i], &keywords);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen break;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen case MAIL_INDEX_SYNC_TYPE_APPEND:
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen case MAIL_INDEX_SYNC_TYPE_EXPUNGE:
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen i_unreached();
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen break;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen }
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen newfname = maildir_filename_set_flags(ctx->keywords_sync_ctx,
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen fname, flags8, &keywords);
b142deb9a831c89b1bb9129ada655f3e56b9d4ccTimo Sirainen newpath = t_strconcat(dir, newfname, NULL);
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen if (rename(path, newpath) == 0) {
b92813e2f96d4b28f989528ed5dd6115da7d9bdbTimo Sirainen if ((flags8 & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
b92813e2f96d4b28f989528ed5dd6115da7d9bdbTimo Sirainen ctx->dirty_state = -1;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->dirty_cur_time = ioloop_time;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 1;
0c17af9d3f9323136a94e66605776ed4462a172dTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (errno == ENOENT)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
b35f7104715edee0cfac6d46ab0b342033867eb7Timo Sirainen if (ENOSPACE(errno) || errno == EACCES) {
b35f7104715edee0cfac6d46ab0b342033867eb7Timo Sirainen mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_ADD,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen MAIL_INDEX_MAIL_FLAG_DIRTY);
b92813e2f96d4b28f989528ed5dd6115da7d9bdbTimo Sirainen ctx->dirty_state = 1;
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen return 1;
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen }
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen
8153fdec343e40e2a78f5c12353e89b994b28f74Timo Sirainen mail_storage_set_critical(STORAGE(mbox->storage),
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "rename(%s, %s) failed: %m", path, newpath);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainenstatic int
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainenmaildir_sync_record_commit_until(struct maildir_index_sync_context *ctx,
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen uint32_t last_seq)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen struct mail_index_sync_rec *recs;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen unsigned int i, count;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen uint32_t seq, uid;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen bool expunged, flag_changed;
659fe5d24825b160cae512538088020d97a60239Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen recs = array_get_modifiable(&ctx->sync_recs, &count);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen for (seq = recs[0].uid1; seq <= last_seq; seq++) {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen expunged = flag_changed = FALSE;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen for (i = 0; i < count; i++) {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (recs[i].uid1 > seq)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen break;
659fe5d24825b160cae512538088020d97a60239Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen i_assert(recs[i].uid1 == seq);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen switch (recs[i].type) {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen case MAIL_INDEX_SYNC_TYPE_EXPUNGE:
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen expunged = TRUE;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen break;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen case MAIL_INDEX_SYNC_TYPE_FLAGS:
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD:
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE:
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen case MAIL_INDEX_SYNC_TYPE_KEYWORD_RESET:
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen flag_changed = TRUE;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen break;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen case MAIL_INDEX_SYNC_TYPE_APPEND:
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen i_unreached();
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen break;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
659fe5d24825b160cae512538088020d97a60239Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (mail_index_lookup_uid(ctx->view, seq, &uid) < 0)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen return -1;
659fe5d24825b160cae512538088020d97a60239Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen ctx->seq = seq;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (expunged) {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (maildir_file_do(ctx->mbox, uid,
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen maildir_expunge, ctx) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen } else if (flag_changed) {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (maildir_file_do(ctx->mbox, uid,
b35f7104715edee0cfac6d46ab0b342033867eb7Timo Sirainen maildir_sync_flags, ctx) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen }
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen for (i = count; i > 0; i--) {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (++recs[i-1].uid1 > recs[i-1].uid2) {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen array_delete(&ctx->sync_recs, i-1, 1);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen recs = array_get_modifiable(&ctx->sync_recs,
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen &count);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (count == 0) {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen /* all sync_recs committed */
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen return 0;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen }
b35f7104715edee0cfac6d46ab0b342033867eb7Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen }
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen return 0;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen}
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainenstatic int maildir_sync_record(struct maildir_index_sync_context *ctx,
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen const struct mail_index_sync_rec *sync_rec)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen{
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen struct mail_index_view *view = ctx->view;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen struct mail_index_sync_rec sync_copy;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (sync_rec == NULL) {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen /* deinit */
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen while (array_count(&ctx->sync_recs) > 0) {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (maildir_sync_record_commit_until(ctx,
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen (uint32_t)-1) < 0)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen return -1;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen }
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen return 0;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen }
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_APPEND)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen return 0; /* ignore */
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen /* convert to sequences to avoid looping through huge holes in
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen UID range */
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen sync_copy = *sync_rec;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (mail_index_lookup_uid_range(view, sync_rec->uid1,
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen sync_rec->uid2,
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen &sync_copy.uid1,
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen &sync_copy.uid2) < 0)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen return -1;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (sync_copy.uid1 == 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* UIDs were expunged */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return 0;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen while (array_count(&ctx->sync_recs) > 0) {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen const struct mail_index_sync_rec *rec =
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen array_idx(&ctx->sync_recs, 0);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen i_assert(rec->uid1 <= sync_copy.uid1);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (rec->uid1 == sync_copy.uid1)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen break;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (maildir_sync_record_commit_until(ctx, sync_copy.uid1-1) < 0)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen array_append(&ctx->sync_recs, &sync_copy, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainenstatic int maildir_sync_index_records(struct maildir_index_sync_context *ctx)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen{
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen struct mail_index_sync_rec sync_rec;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen int ret;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen ret = mail_index_sync_next(ctx->sync_ctx, &sync_rec);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (ret <= 0)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen return ret;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen t_array_init(&ctx->sync_recs, 32);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen do {
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (maildir_sync_record(ctx, &sync_rec) < 0)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen return -1;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen ret = mail_index_sync_next(ctx->sync_ctx, &sync_rec);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen } while (ret > 0);
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen if (maildir_sync_record(ctx, NULL) < 0)
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen return -1;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen return ret;
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen}
7fb7365a8fad104a17538a73c338ee3d3420e7b0Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic struct maildir_sync_context *
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenmaildir_sync_context_new(struct maildir_mailbox *mbox)
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;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen ctx->new_dir = t_strconcat(mbox->path, "/new", NULL);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen ctx->cur_dir = t_strconcat(mbox->path, "/cur", NULL);
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)
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen (void)maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx);
0b3662995e9fa0d0d857ec5350ce2b1ee6d3b94fTimo Sirainen if (ctx->index_sync_ctx != NULL) {
0b3662995e9fa0d0d857ec5350ce2b1ee6d3b94fTimo Sirainen (void)maildir_sync_index_finish(&ctx->index_sync_ctx,
0b3662995e9fa0d0d857ec5350ce2b1ee6d3b94fTimo Sirainen TRUE, FALSE);
0b3662995e9fa0d0d857ec5350ce2b1ee6d3b94fTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainenstatic int maildir_fix_duplicate(struct maildir_sync_context *ctx,
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen const char *dir, const char *old_fname)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen struct maildir_mailbox *mbox = ctx->mbox;
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen const char *existing_fname, *existing_path;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *new_fname, *old_path, *new_path;
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen struct stat st, st2;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int ret = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen existing_fname =
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen maildir_uidlist_sync_get_full_filename(ctx->uidlist_sync_ctx,
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen old_fname);
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen i_assert(existing_fname != NULL);
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_push();
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen existing_path = t_strconcat(dir, "/", existing_fname, NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen old_path = t_strconcat(dir, "/", old_fname, NULL);
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen if (stat(existing_path, &st) < 0 ||
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen stat(old_path, &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 t_pop();
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen return 0;
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen }
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen if (st.st_ino == st2.st_ino && CMP_DEV_T(st.st_dev, st2.st_dev)) {
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen /* files are the same. this means either a race condition
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen between stat() calls, or someone has started link()ing the
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen files. either way there's no data loss if we just leave it
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen there. */
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen t_pop();
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen return 0;
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen }
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen new_fname = maildir_generate_tmp_filename(&ioloop_timeval);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen new_path = t_strconcat(mbox->path, "/new/", new_fname, NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (rename(old_path, new_path) == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_warning("Fixed duplicate in %s: %s -> %s",
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->path, old_fname, new_fname);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else if (errno != ENOENT) {
8153fdec343e40e2a78f5c12353e89b994b28f74Timo Sirainen mail_storage_set_critical(STORAGE(mbox->storage),
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "rename(%s, %s) failed: %m", old_path, new_path);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_pop();
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenstatic int maildir_scan_dir(struct maildir_sync_context *ctx, bool new_dir)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
8153fdec343e40e2a78f5c12353e89b994b28f74Timo Sirainen struct mail_storage *storage = STORAGE(ctx->mbox->storage);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *dir;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen DIR *dirp;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen string_t *src, *dest;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct dirent *dp;
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen enum maildir_uidlist_rec_flag flags;
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen unsigned int moves = 0;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen int ret = 1;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen bool move_new;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dir = new_dir ? ctx->new_dir : ctx->cur_dir;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dirp = opendir(dir);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (dirp == NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_storage_set_critical(storage,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "opendir(%s) failed: %m", dir);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen t_push();
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen src = t_str_new(1024);
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen dest = t_str_new(1024);
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen move_new = new_dir && !mailbox_is_readonly(&ctx->mbox->ibox.box) &&
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen !ctx->mbox->ibox.keep_recent;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen while ((dp = readdir(dirp)) != NULL) {
7bd6001d84ecc1792ddfd54fe8efa63c509d90b1Timo Sirainen if (dp->d_name[0] == '.')
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen continue;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen ret = maildir_uidlist_sync_next_pre(ctx->uidlist_sync_ctx,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen dp->d_name);
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen if (ret == 0) {
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen /* new file and we couldn't lock uidlist, check this
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen later in next sync. */
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen if (new_dir)
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen ctx->mbox->last_new_mtime = 0;
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen else
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen ctx->mbox->dirty_cur_time = ioloop_time;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen continue;
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen }
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen if (ret < 0)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen break;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen flags = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (move_new) {
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 */
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen moves++;
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen ctx->mbox->dirty_cur_time = ioloop_time;
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 */
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen moves++;
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen flags |= MAILDIR_UIDLIST_REC_FLAG_MOVED;
9a099a65160987349f441c82ab0e38f32b747adbTimo Sirainen } else if (ENOSPACE(errno) || errno == EACCES) {
9a099a65160987349f441c82ab0e38f32b747adbTimo Sirainen /* not enough disk space / read-only maildir,
9a099a65160987349f441c82ab0e38f32b747adbTimo Sirainen leave here */
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen MAILDIR_UIDLIST_REC_FLAG_RECENT;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen move_new = FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else {
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen MAILDIR_UIDLIST_REC_FLAG_RECENT;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_storage_set_critical(storage,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "rename(%s, %s) failed: %m",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_c(src), str_c(dest));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
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
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 */
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainen if (maildir_fix_duplicate(ctx, dir, dp->d_name) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen break;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (closedir(dirp) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_storage_set_critical(storage,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "closedir(%s) failed: %m", dir);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen
d77c309fccbc6a7594f8cb08fb01009fa613c568Timo Sirainen t_pop();
b66a7b7ab0db2c9ad425912d3f21a36fcf76d876Timo Sirainen return ret < 0 ? -1 : (moves <= MAILDIR_RENAME_RESCAN_COUNT ? 0 : 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainenstatic void
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainenmaildir_sync_update_from_header(struct maildir_mailbox *mbox,
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen struct mail_index_header *hdr_r)
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen{
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen struct mail_index_view *view;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen const struct mail_index_header *hdr;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen /* open a new view so we get the latest header */
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen view = mail_index_view_open(mbox->ibox.index);
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen hdr = mail_index_get_header(view);
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen /* FIXME: ugly, replace with extension header */
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen mbox->last_new_mtime = hdr->sync_size & 0xffffffff;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen mbox->last_dirty_flags = (hdr->sync_size >> 32) &
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen (MAILDIR_DIRTY_NEW | MAILDIR_DIRTY_CUR);
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen mbox->last_cur_mtime = hdr->sync_stamp;
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen if ((mbox->last_dirty_flags & MAILDIR_DIRTY_CUR) != 0 &&
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen mbox->dirty_cur_time < mbox->last_cur_mtime)
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen mbox->dirty_cur_time = mbox->last_cur_mtime;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen *hdr_r = *hdr;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen mail_index_view_close(&view);
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen}
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainenstatic int
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainenmaildir_sync_quick_check(struct maildir_mailbox *mbox,
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen const char *new_dir, const char *cur_dir,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen bool *new_changed_r, bool *cur_changed_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
ad8b3389ab62c5facb3d49037963076e3d32c147Timo Sirainen struct index_mailbox *ibox = &mbox->ibox;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen struct mail_index_header hdr;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct stat st;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen time_t new_mtime, cur_mtime;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *new_changed_r = *cur_changed_r = FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen if (stat(new_dir, &st) < 0) {
8153fdec343e40e2a78f5c12353e89b994b28f74Timo Sirainen mail_storage_set_critical(STORAGE(mbox->storage),
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen "stat(%s) failed: %m", new_dir);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen new_mtime = st.st_mtime;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen if (stat(cur_dir, &st) < 0) {
8153fdec343e40e2a78f5c12353e89b994b28f74Timo Sirainen mail_storage_set_critical(STORAGE(mbox->storage),
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen "stat(%s) failed: %m", cur_dir);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cur_mtime = st.st_mtime;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
abbe0657a4d6271740d41cf1de55e8686f5769fcTimo Sirainen /* cur stamp is kept in index, we don't have to sync if
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen someone else has done it and updated the index.
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen FIXME: For now we're using sync_size field as the new/ dir's stamp.
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen Pretty ugly.. */
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen maildir_sync_update_from_header(mbox, &hdr);
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen if ((mbox->dirty_cur_time == 0 && cur_mtime != mbox->last_cur_mtime) ||
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen (new_mtime != mbox->last_new_mtime)) {
abbe0657a4d6271740d41cf1de55e8686f5769fcTimo Sirainen /* check if the index has been updated.. */
ad8b3389ab62c5facb3d49037963076e3d32c147Timo Sirainen if (mail_index_refresh(ibox->index) < 0) {
ad8b3389ab62c5facb3d49037963076e3d32c147Timo Sirainen mail_storage_set_index_error(ibox);
bde6382cf65fba6165dc3603f5419e194d87f404Timo Sirainen return -1;
bde6382cf65fba6165dc3603f5419e194d87f404Timo Sirainen }
bde6382cf65fba6165dc3603f5419e194d87f404Timo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen maildir_sync_update_from_header(mbox, &hdr);
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen }
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen
ad8b3389ab62c5facb3d49037963076e3d32c147Timo Sirainen /* If we're removing recent flags, always sync new/ directory if
ad8b3389ab62c5facb3d49037963076e3d32c147Timo Sirainen it has mails. */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (new_mtime != mbox->last_new_mtime ||
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen ((mbox->last_dirty_flags & MAILDIR_DIRTY_NEW) != 0 &&
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen new_mtime < ioloop_time - MAILDIR_SYNC_SECS) ||
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen (!ibox->keep_recent && hdr.recent_messages_count > 0)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *new_changed_r = TRUE;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->last_new_mtime = new_mtime;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen if (new_mtime < ioloop_time - MAILDIR_SYNC_SECS)
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen mbox->last_dirty_flags &= ~MAILDIR_DIRTY_NEW;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen else
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen mbox->last_dirty_flags |= MAILDIR_DIRTY_NEW;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (cur_mtime != mbox->last_cur_mtime ||
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen (mbox->dirty_cur_time != 0 &&
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen ioloop_time - mbox->dirty_cur_time > MAILDIR_SYNC_SECS)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* cur/ changed, or delayed cur/ check */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *cur_changed_r = TRUE;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->last_cur_mtime = cur_mtime;
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen if (cur_mtime < ioloop_time - MAILDIR_SYNC_SECS) {
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen mbox->last_dirty_flags &= ~MAILDIR_DIRTY_CUR;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen mbox->dirty_cur_time = 0;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen } else {
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen mbox->last_dirty_flags |= MAILDIR_DIRTY_CUR;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen mbox->dirty_cur_time = cur_mtime;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainenint maildir_sync_index_begin(struct maildir_mailbox *mbox,
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen struct maildir_index_sync_context **ctx_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen struct maildir_index_sync_context *ctx;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen struct mail_index_sync_ctx *sync_ctx;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen struct mail_index_view *view;
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen if (mail_index_sync_begin(mbox->ibox.index, &sync_ctx, &view,
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen (uint32_t)-1, (uoff_t)-1,
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen FALSE, FALSE) <= 0) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mail_storage_set_index_error(&mbox->ibox);
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen return -1;
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen ctx = i_new(struct maildir_index_sync_context, 1);
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen ctx->mbox = mbox;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen ctx->sync_ctx = sync_ctx;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen ctx->view = view;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen ctx->keywords_sync_ctx =
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen maildir_keywords_sync_init(mbox->keywords, mbox->ibox.index);
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen *ctx_r = ctx;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen return 0;
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen}
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainenint maildir_sync_index_finish(struct maildir_index_sync_context **_sync_ctx,
0b3662995e9fa0d0d857ec5350ce2b1ee6d3b94fTimo Sirainen bool failed, bool cancel)
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen{
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen struct maildir_index_sync_context *sync_ctx = *_sync_ctx;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen struct maildir_mailbox *mbox = sync_ctx->mbox;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen uint32_t seq;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen uoff_t offset;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen int ret = failed ? -1 : 0;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen *_sync_ctx = NULL;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen if (sync_ctx->trans != NULL) {
0b3662995e9fa0d0d857ec5350ce2b1ee6d3b94fTimo Sirainen if (ret < 0 || cancel)
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen mail_index_transaction_rollback(&sync_ctx->trans);
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen else {
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen if (mail_index_transaction_commit(&sync_ctx->trans,
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen &seq, &offset) < 0)
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen ret = -1;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen else if (seq != 0) {
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen mbox->ibox.commit_log_file_seq = seq;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen mbox->ibox.commit_log_file_offset = offset;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen }
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen }
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen }
0b3662995e9fa0d0d857ec5350ce2b1ee6d3b94fTimo Sirainen if (ret < 0 || cancel)
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen mail_index_sync_rollback(&sync_ctx->sync_ctx);
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen else {
947a2df7c6a2c48711b051736e08402f6d91795cTimo Sirainen /* Set syncing_commit=TRUE so that if any sync callbacks try
947a2df7c6a2c48711b051736e08402f6d91795cTimo Sirainen to access mails which got lost (eg. expunge callback trying
947a2df7c6a2c48711b051736e08402f6d91795cTimo Sirainen to open the file which was just unlinked) we don't try to
947a2df7c6a2c48711b051736e08402f6d91795cTimo Sirainen start a second index sync and crash. */
947a2df7c6a2c48711b051736e08402f6d91795cTimo Sirainen mbox->syncing_commit = TRUE;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen if (mail_index_sync_commit(&sync_ctx->sync_ctx) < 0)
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen ret = -1;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen else {
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen mbox->ibox.commit_log_file_seq = 0;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen mbox->ibox.commit_log_file_offset = 0;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen }
947a2df7c6a2c48711b051736e08402f6d91795cTimo Sirainen mbox->syncing_commit = FALSE;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen }
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen if (ret < 0)
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen mail_storage_set_index_error(&mbox->ibox);
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen maildir_keywords_sync_deinit(sync_ctx->keywords_sync_ctx);
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen sync_ctx->keywords_sync_ctx = NULL;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen i_free(sync_ctx);
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen return ret;
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen}
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainenint maildir_sync_index(struct maildir_index_sync_context *sync_ctx,
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen bool partial)
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen{
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen struct maildir_mailbox *mbox = sync_ctx->mbox;
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen struct mail_index_view *view = sync_ctx->view;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct maildir_uidlist_iter_ctx *iter;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_index_transaction *trans;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_index_header *hdr;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_index_record *rec;
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen uint32_t seq, uid;
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen enum maildir_uidlist_rec_flag uflags;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *filename;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen enum mail_flags flags;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen ARRAY_TYPE(keyword_indexes) keywords;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen ARRAY_TYPE(keyword_indexes) idx_keywords;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen uint32_t uid_validity, next_uid;
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen uint64_t value;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen int ret = 0;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen bool full_rescan = FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
7ba90b7ec9e0a97c01e89e2714ff54e35c5ddfbfTimo Sirainen i_assert(maildir_uidlist_is_locked(sync_ctx->mbox->uidlist));
7ba90b7ec9e0a97c01e89e2714ff54e35c5ddfbfTimo Sirainen
b20fb5b1df9d604a7541f5118fc5b4b466d211efTimo Sirainen hdr = mail_index_get_header(view);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen uid_validity = maildir_uidlist_get_uid_validity(mbox->uidlist);
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainen if (uid_validity != hdr->uid_validity &&
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainen uid_validity != 0 && hdr->uid_validity != 0) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen /* uidvalidity changed and mailbox isn't being initialized,
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen reset mailbox so we can add all messages as new */
8153fdec343e40e2a78f5c12353e89b994b28f74Timo Sirainen mail_storage_set_critical(STORAGE(mbox->storage),
6969f13267ad8495a132ece39d34be36b9883f37Timo Sirainen "Maildir %s sync: UIDVALIDITY changed (%u -> %u)",
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->path, hdr->uid_validity, uid_validity);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
cdaf255d6a3daeef0ac85edaa60bfa6d1f945bffTimo Sirainen mail_index_mark_corrupted(mbox->ibox.index);
cdaf255d6a3daeef0ac85edaa60bfa6d1f945bffTimo Sirainen return -1;
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen sync_ctx->trans = trans =
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen mail_index_transaction_begin(sync_ctx->view, FALSE, TRUE);
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen seq = 0;
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen t_array_init(&keywords, MAILDIR_MAX_KEYWORDS);
91dca97b367c54a139c268b56a0c67f564bd9197Timo Sirainen t_array_init(&idx_keywords, MAILDIR_MAX_KEYWORDS);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen iter = maildir_uidlist_iter_init(mbox->uidlist);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen while (maildir_uidlist_iter_next(iter, &uid, &uflags, &filename)) {
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen maildir_filename_get_flags(sync_ctx->keywords_sync_ctx,
e1ca7af110ea6eeb6303bdd8f07c172b11dff2faTimo Sirainen filename, &flags, &keywords);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
24ddec4ee3571be32f615138b56cf0ae6f922af0Timo Sirainen /* the private flags are kept only in indexes. don't use them
24ddec4ee3571be32f615138b56cf0ae6f922af0Timo Sirainen at all even for newly seen mails */
24ddec4ee3571be32f615138b56cf0ae6f922af0Timo Sirainen flags &= ~mbox->private_flags_mask;
24ddec4ee3571be32f615138b56cf0ae6f922af0Timo Sirainen
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen if ((uflags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0 &&
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen (uflags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0 &&
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen (uflags & MAILDIR_UIDLIST_REC_FLAG_MOVED) == 0) {
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen /* mail is recent for next session as well */
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen flags |= MAIL_RECENT;
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen }
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen __again:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen seq++;
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (seq > hdr->messages_count) {
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen if (uid < hdr->next_uid) {
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen /* most likely a race condition: we read the
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen maildir, then someone else expunged messages
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen and committed changes to index. so, this
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen message shouldn't actually exist. mark it
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen racy and check in next sync.
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen the difference between this and the later
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen check is that this one happens when messages
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen are expunged from the end */
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen if ((uflags &
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) {
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen /* partial syncing */
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen continue;
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen }
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen if ((uflags &
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen MAILDIR_UIDLIST_REC_FLAG_RACING) != 0) {
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen mail_storage_set_critical(
8153fdec343e40e2a78f5c12353e89b994b28f74Timo Sirainen STORAGE(mbox->storage),
6969f13267ad8495a132ece39d34be36b9883f37Timo Sirainen "Maildir %s sync: "
6969f13267ad8495a132ece39d34be36b9883f37Timo Sirainen "UID < next_uid "
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen "(%u < %u, file = %s)",
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->path, uid, hdr->next_uid,
6969f13267ad8495a132ece39d34be36b9883f37Timo Sirainen filename);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mail_index_mark_corrupted(
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->ibox.index);
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen ret = -1;
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen break;
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen }
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->dirty_cur_time = ioloop_time;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen maildir_uidlist_add_flags(mbox->uidlist,
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen filename,
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen MAILDIR_UIDLIST_REC_FLAG_RACING);
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen seq--;
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen continue;
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen }
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_append(trans, uid, &seq);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_update_flags(trans, seq, MODIFY_REPLACE,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen flags);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (array_count(&keywords) > 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct mail_keywords *kw;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen kw = mail_index_keywords_create_from_indexes(
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen trans, &keywords);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen mail_index_update_keywords(trans, seq,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen MODIFY_REPLACE, kw);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen mail_index_keywords_free(&kw);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen continue;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (mail_index_lookup(view, seq, &rec) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen break;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (rec->uid < uid) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* expunged */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_expunge(trans, seq);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen goto __again;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (rec->uid > uid) {
a038139a470d2942759b9b86a9852aee7b460996Timo Sirainen /* most likely a race condition: we read the
a038139a470d2942759b9b86a9852aee7b460996Timo Sirainen maildir, then someone else expunged messages and
a038139a470d2942759b9b86a9852aee7b460996Timo Sirainen committed changes to index. so, this message
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen shouldn't actually exist. mark it racy and check
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen in next sync. */
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen if ((uflags &
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) {
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen /* partial syncing */
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen seq--;
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen continue;
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen }
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen if ((uflags & MAILDIR_UIDLIST_REC_FLAG_RACING) != 0) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mail_storage_set_critical(
8153fdec343e40e2a78f5c12353e89b994b28f74Timo Sirainen STORAGE(mbox->storage),
0e2686dfe29a18772fa4026bad53e2c7c560403fTimo Sirainen "Maildir %s sync: "
0e2686dfe29a18772fa4026bad53e2c7c560403fTimo Sirainen "UID inserted in the middle of mailbox "
a038139a470d2942759b9b86a9852aee7b460996Timo Sirainen "(%u > %u, file = %s)",
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->path, rec->uid, uid, filename);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mail_index_mark_corrupted(mbox->ibox.index);
a038139a470d2942759b9b86a9852aee7b460996Timo Sirainen ret = -1;
a038139a470d2942759b9b86a9852aee7b460996Timo Sirainen break;
bde6382cf65fba6165dc3603f5419e194d87f404Timo Sirainen }
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->dirty_cur_time = ioloop_time;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen maildir_uidlist_add_flags(mbox->uidlist, filename,
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainen MAILDIR_UIDLIST_REC_FLAG_RACING);
a038139a470d2942759b9b86a9852aee7b460996Timo Sirainen
a038139a470d2942759b9b86a9852aee7b460996Timo Sirainen seq--;
a038139a470d2942759b9b86a9852aee7b460996Timo Sirainen continue;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
24ddec4ee3571be32f615138b56cf0ae6f922af0Timo Sirainen /* the private flags are stored only in indexes, keep them */
24ddec4ee3571be32f615138b56cf0ae6f922af0Timo Sirainen flags |= rec->flags & mbox->private_flags_mask;
24ddec4ee3571be32f615138b56cf0ae6f922af0Timo Sirainen
0aa66a8e037c08604e71446d6ba52150a17dfc21Timo Sirainen if ((rec->flags & MAIL_RECENT) != 0) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen index_mailbox_set_recent(&mbox->ibox, seq);
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (mbox->ibox.keep_recent) {
0aa66a8e037c08604e71446d6ba52150a17dfc21Timo Sirainen flags |= MAIL_RECENT;
0aa66a8e037c08604e71446d6ba52150a17dfc21Timo Sirainen } else {
0aa66a8e037c08604e71446d6ba52150a17dfc21Timo Sirainen mail_index_update_flags(trans, seq,
0aa66a8e037c08604e71446d6ba52150a17dfc21Timo Sirainen MODIFY_REMOVE,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen MAIL_RECENT);
0aa66a8e037c08604e71446d6ba52150a17dfc21Timo Sirainen }
0aa66a8e037c08604e71446d6ba52150a17dfc21Timo Sirainen }
0aa66a8e037c08604e71446d6ba52150a17dfc21Timo Sirainen
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen if ((uflags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) {
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen /* partial syncing */
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen if ((flags & MAIL_RECENT) != 0) {
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen /* we last saw this mail in new/, but it's
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen not there anymore. possibly expunged,
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen make sure. */
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen full_rescan = TRUE;
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen }
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen continue;
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen }
a33fde36c57839342dafdf3c8d4b5c009e3ef4bcTimo Sirainen
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) {
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen /* we haven't been able to update maildir with this
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen record's flag changes. don't sync them. */
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen continue;
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen }
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen if (((uint8_t)flags & ~MAIL_RECENT) !=
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen (rec->flags & (MAIL_FLAGS_MASK^MAIL_RECENT))) {
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen /* FIXME: this is wrong if there's pending changes in
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen transaction log already. it gets fixed in next sync
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen however.. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_update_flags(trans, seq, MODIFY_REPLACE,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen flags);
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen } else if ((flags & MAIL_RECENT) == 0 &&
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen (rec->flags & MAIL_RECENT) != 0) {
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen /* just remove recent flag */
d482b35af87f5fd872bad007da0475813a401a49Timo Sirainen mail_index_update_flags(trans, seq, MODIFY_REMOVE,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen MAIL_RECENT);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen /* update keywords if they have changed */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (mail_index_lookup_keywords(view, seq, &idx_keywords) < 0) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen ret = -1;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen break;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
bb26f09873c18f342cd1ab2d0ee0b9018e6546d9Timo Sirainen if (!index_keyword_array_cmp(&keywords, &idx_keywords)) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct mail_keywords *kw;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen kw = mail_index_keywords_create_from_indexes(
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen trans, &keywords);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen mail_index_update_keywords(trans, seq,
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen MODIFY_REPLACE, kw);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen mail_index_keywords_free(&kw);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen maildir_uidlist_iter_deinit(iter);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen array_free(&keywords);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
cad14cfd254a89936d07137a25b7caaf5e670664Timo Sirainen if (!partial) {
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen /* expunge the rest */
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen for (seq++; seq <= hdr->messages_count; seq++)
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen mail_index_expunge(trans, seq);
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen /* next_uid must be updated only in non-partial syncs since
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen partial syncs don't add the new mails to index. also we'll
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen have to do it here before syncing index records, since after
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen that the uidlist's next_uid value may have changed. */
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen next_uid = maildir_uidlist_get_next_uid(mbox->uidlist);
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen if (next_uid != 0 && hdr->next_uid != next_uid) {
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen mail_index_update_header(trans,
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen offsetof(struct mail_index_header, next_uid),
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen &next_uid, sizeof(next_uid), FALSE);
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen }
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen }
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen if (!mbox->syncing_commit) {
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen /* now, sync the index. NOTE: may recurse back to here with
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen partial syncs */
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen mbox->syncing_commit = TRUE;
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen if (maildir_sync_index_records(sync_ctx) < 0)
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen ret = -1;
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen mbox->syncing_commit = FALSE;
bd606153e12c04799be8f27933b5c983db0eaa15Timo Sirainen }
b35f7104715edee0cfac6d46ab0b342033867eb7Timo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen if (mbox->dirty_cur_time != 0)
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen mbox->last_dirty_flags |= MAILDIR_DIRTY_CUR;
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen if (mbox->last_cur_mtime != (time_t)hdr->sync_stamp) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen uint32_t sync_stamp = mbox->last_cur_mtime;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen mail_index_update_header(trans,
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen offsetof(struct mail_index_header, sync_stamp),
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen &sync_stamp, sizeof(sync_stamp), TRUE);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen }
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen /* FIXME: use a header extension instead of sync_size.. */
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen value = mbox->last_new_mtime |
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen ((uint64_t)mbox->last_dirty_flags << 32);
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen if (value != hdr->sync_size) {
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen mail_index_update_header(trans,
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen offsetof(struct mail_index_header, sync_size),
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen &value, sizeof(value), TRUE);
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen }
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainen if (hdr->uid_validity == 0) {
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainen /* get the initial uidvalidity */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (maildir_uidlist_update(mbox->uidlist) < 0)
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainen ret = -1;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen uid_validity = maildir_uidlist_get_uid_validity(mbox->uidlist);
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainen if (uid_validity == 0) {
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainen uid_validity = ioloop_time;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen maildir_uidlist_set_uid_validity(mbox->uidlist,
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainen uid_validity);
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainen }
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainen } else if (uid_validity == 0) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen maildir_uidlist_set_uid_validity(mbox->uidlist,
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainen hdr->uid_validity);
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainen }
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainen
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainen if (uid_validity != hdr->uid_validity && uid_validity != 0) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen mail_index_update_header(trans,
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen offsetof(struct mail_index_header, uid_validity),
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen &uid_validity, sizeof(uid_validity), TRUE);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen }
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen return ret < 0 ? -1 : (full_rescan ? 0 : 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenstatic int maildir_sync_context(struct maildir_sync_context *ctx, bool forced,
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen bool sync_last_commit)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen bool new_changed, cur_changed, full_rescan = FALSE;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen int ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (sync_last_commit) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen new_changed = cur_changed = FALSE;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen } else if (!forced) {
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen if (maildir_sync_quick_check(ctx->mbox,
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen ctx->new_dir, ctx->cur_dir,
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen &new_changed, &cur_changed) < 0)
a38ef15060e45e5060bb24c403a580b9a57a818cTimo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
a38ef15060e45e5060bb24c403a580b9a57a818cTimo Sirainen if (!new_changed && !cur_changed)
4fd4705bbc6553ae0959bea07f33d0b7249391f5Timo Sirainen return 1;
a38ef15060e45e5060bb24c403a580b9a57a818cTimo Sirainen } else {
a38ef15060e45e5060bb24c403a580b9a57a818cTimo Sirainen new_changed = cur_changed = TRUE;
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
57a91f930a12d2cd1220da4f3f7cb2c47557cd37Timo Sirainen ctx->partial = !cur_changed;
57a91f930a12d2cd1220da4f3f7cb2c47557cd37Timo Sirainen ret = maildir_uidlist_sync_init(ctx->mbox->uidlist, ctx->partial,
57a91f930a12d2cd1220da4f3f7cb2c47557cd37Timo Sirainen &ctx->uidlist_sync_ctx);
57a91f930a12d2cd1220da4f3f7cb2c47557cd37Timo Sirainen if (ret <= 0) {
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen /* failure / timeout. if forced is TRUE, we could still go
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen forward and check only for renamed files, but is it worth
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen the trouble? .. */
d4dcb9c30dba354cff7af6d303ecef7698194c55Timo Sirainen return ret;
a38ef15060e45e5060bb24c403a580b9a57a818cTimo Sirainen }
73c76fa7340a107229c530196d026aadeae979c7Timo Sirainen
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen if (!ctx->mbox->syncing_commit) {
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen if (maildir_sync_index_begin(ctx->mbox,
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. */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen while ((ret = maildir_scan_dir(ctx, TRUE)) > 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). */
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (ret < 0)
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen return -1;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (cur_changed) {
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (maildir_scan_dir(ctx, FALSE) < 0)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return -1;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen }
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo 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
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen if (!ctx->mbox->syncing_commit) {
57a91f930a12d2cd1220da4f3f7cb2c47557cd37Timo Sirainen /* NOTE: index syncing here might cause a re-sync due to
57a91f930a12d2cd1220da4f3f7cb2c47557cd37Timo Sirainen files getting lost, so this function might be called
57a91f930a12d2cd1220da4f3f7cb2c47557cd37Timo Sirainen re-entrantly. FIXME: and that breaks in
57a91f930a12d2cd1220da4f3f7cb2c47557cd37Timo Sirainen maildir_uidlist_sync_deinit() */
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen ret = maildir_sync_index(ctx->index_sync_ctx, ctx->partial);
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen if (maildir_sync_index_finish(&ctx->index_sync_ctx,
0b3662995e9fa0d0d857ec5350ce2b1ee6d3b94fTimo Sirainen ret < 0, FALSE) < 0)
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen return -1;
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen if (ret < 0)
65d6ca3fb5450b81df0190d9e9aa62c00fed5116Timo Sirainen return -1;
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen if (ret == 0)
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen full_rescan = TRUE;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen i_assert(maildir_uidlist_is_locked(ctx->mbox->uidlist));
65d6ca3fb5450b81df0190d9e9aa62c00fed5116Timo Sirainen }
d67f54632110cfb6aafe2d7cd1f99b031c0b208aTimo Sirainen
e05181d973025627ba08b631c12c07c3bbc99528Timo Sirainen ret = maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx);
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen return ret < 0 ? -1 : (full_rescan ? 0 : 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenint maildir_storage_sync_force(struct maildir_mailbox *mbox)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct maildir_sync_context *ctx;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen ctx = maildir_sync_context_new(mbox);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen ret = maildir_sync_context(ctx, TRUE, FALSE);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen maildir_sync_deinit(ctx);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return ret < 0 ? -1 : 0;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen}
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainenint maildir_sync_last_commit(struct maildir_mailbox *mbox)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen{
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen struct maildir_sync_context *ctx;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen int ret;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen if (mbox->ibox.commit_log_file_seq == 0)
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen return 0;
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen ctx = maildir_sync_context_new(mbox);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen ret = maildir_sync_context(ctx, FALSE, TRUE);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen maildir_sync_deinit(ctx);
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen return ret < 0 ? -1 : 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainenstruct mailbox_sync_context *
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainenmaildir_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct maildir_sync_context *ctx;
d756ebcfa96bd7cff02097c8f26df9df368b81b1Timo Sirainen int ret = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen if (!box->opened) {
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen if (index_storage_mailbox_open(&mbox->ibox) < 0)
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen return index_mailbox_sync_init(box, 0, TRUE);
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen }
5fb3bff645380804c9db2510940c41db6b8fdb01Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((flags & MAILBOX_SYNC_FLAG_FAST) == 0 ||
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->ibox.sync_last_check + MAILBOX_FULL_SYNC_INTERVAL <=
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen ioloop_time) {
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen mbox->ibox.sync_last_check = ioloop_time;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen ctx = maildir_sync_context_new(mbox);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen ret = maildir_sync_context(ctx, FALSE, FALSE);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen maildir_sync_deinit(ctx);
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen
d67ac5f76cc02c227f4997878bb4aef48ee298faTimo Sirainen i_assert(!maildir_uidlist_is_locked(mbox->uidlist) ||
d67ac5f76cc02c227f4997878bb4aef48ee298faTimo Sirainen mbox->ibox.keep_locked);
cce169a321c9c629e4f2db1a69dae3b75bbcb27aTimo Sirainen
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen if (ret == 0) {
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen /* lost some files from new/, see if thery're in cur/ */
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen ret = maildir_storage_sync_force(mbox);
4d938f46f4f956ecb802c30ca771922f5539a660Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
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{
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen const char *new_dir, *cur_dir;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen bool new_changed, cur_changed;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen int ret;
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen t_push();
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen new_dir = t_strconcat(mbox->path, "/new", NULL);
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen cur_dir = t_strconcat(mbox->path, "/cur", NULL);
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen ret = maildir_sync_quick_check(mbox, new_dir, cur_dir,
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen &new_changed, &cur_changed);
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen t_pop();
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen return ret < 0 ? -1 : (!new_changed && !cur_changed);
a3d22d3cb0e5436128ca7287cedc921f1789b2c8Timo Sirainen}