maildir-sync.c revision 6f66e585998aa88a4b0ccad531d329a103325d57
a8c5a86d183db25a57bf193c06b41e092ec2e151Timo Sirainen/* Copyright (C) 2004 Timo Sirainen */
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen Here's a description of how we handle Maildir synchronization and
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen it's problems:
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen We want to be as efficient as we can. The most efficient way to
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen check if changes have occurred is to stat() the new/ and cur/
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen directories and uidlist file - if their mtimes haven't changed,
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen there's no changes and we don't need to do anything.
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen Problem 1: Multiple changes can happen within a single second -
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen nothing guarantees that once we synced it, someone else didn't just
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen then make a modification. Such modifications wouldn't get noticed
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen until a new modification occurred later.
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen Problem 2: Syncing cur/ directory is much more costly than syncing
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen new/. Moving mails from new/ to cur/ will always change mtime of
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen cur/ causing us to sync it as well.
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen Problem 3: We may not be able to move mail from new/ to cur/
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen because we're out of quota, or simply because we're accessing a
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen read-only mailbox.
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen MAILDIR_SYNC_SECS
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen -----------------
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen Several checks below use MAILDIR_SYNC_SECS, which should be maximum
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen clock drift between all computers accessing the maildir (eg. via
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen NFS), rounded up to next second. Our default is 1 second, since
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen everyone should be using NTP.
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen Note that setting it to 0 works only if there's only one computer
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen accessing the maildir. It's practically impossible to make two
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen clocks _exactly_ synchronized.
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen It might be possible to only use file server's clock by looking at
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen the atime field, but I don't know how well that would actually work.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen cur directory
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen -------------
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen We have dirty_cur_time variable which is set to cur/ directory's
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen mtime when it's >= time() - MAILDIR_SYNC_SECS and we _think_ we have
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen synchronized the directory.
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen When dirty_cur_time is non-zero, we don't synchronize the cur/
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen directory until
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen a) cur/'s mtime changes
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen b) opening a mail fails with ENOENT
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen c) time() > dirty_cur_time + MAILDIR_SYNC_SECS
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen This allows us to modify the maildir multiple times without having
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen to sync it at every change. The sync will eventually be done to
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen make sure we didn't miss any external changes.
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen The dirty_cur_time is set when:
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen - we change message flags
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen - we expunge messages
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen - we move mail from new/ to cur/
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen - we sync cur/ directory and it's mtime is >= time() - MAILDIR_SYNC_SECS
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen It's unset when we do the final syncing, ie. when mtime is
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen older than time() - MAILDIR_SYNC_SECS.
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen new directory
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen -------------
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen If new/'s mtime is >= time() - MAILDIR_SYNC_SECS, always synchronize
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen it. dirty_cur_time-like feature might save us a few syncs, but
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen that might break a client which saves a mail in one connection and
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen tries to fetch it in another one. new/ directory is almost always
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen empty, so syncing it should be very fast anyway. Actually this can
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen still happen if we sync only new/ dir while another client is also
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen moving mails from it to cur/ - it takes us a while to see them.
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen That's pretty unlikely to happen however, and only way to fix it
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen would be to always synchronize cur/ after new/.
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen Normally we move all mails from new/ to cur/ whenever we sync it. If
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen it's not possible for some reason, we mark the mail with "probably
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen exists in new/ directory" flag.
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen If rename() still fails because of ENOSPC or EDQUOT, we still save
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen the flag changes in index with dirty-flag on. When moving the mail
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen to cur/ directory, or when we notice it's already moved there, we
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen apply the flag changes to the filename, rename it and remove the
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen dirty flag. If there's dirty flags, this should be tried every time
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen after expunge or when closing the mailbox.
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen This file contains UID <-> filename mappings. It's updated only when
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen new mail arrives, so it may contain filenames that have already been
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen deleted. Updating is done by getting uidlist.lock file, writing the
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen whole uidlist into it and rename()ing it over the old uidlist. This
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen means there's no need to lock the file for reading.
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen Whenever uidlist is rewritten, it's mtime must be larger than the old
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen one's. Use utime() before rename() if needed. Note that inode checking
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen wouldn't have been sufficient as inode numbers can be reused.
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen This file is usually read the first time you need to know filename for
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen given UID. After that it's not re-read unless new mails come that we
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen don't know about.
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen broken clients
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen --------------
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen Originally the middle identifier in Maildir filename was specified
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen only as <process id>_<delivery counter>. That however created a
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen problem with randomized PIDs which made it possible that the same
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen PID was reused within one second.
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen So if within one second a mail was delivered, MUA moved it to cur/
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen and another mail was delivered by a new process using same PID as
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen the first one, we likely ended up overwriting the first mail when
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen the second mail was moved over it.
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen Nowadays everyone should be giving a bit more specific identifier,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen for example include microseconds in it which Dovecot does.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen There's a simple way to prevent this from happening in some cases:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen Don't move the mail from new/ to cur/ if it's mtime is >= time() -
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen MAILDIR_SYNC_SECS. The second delivery's link() call then fails
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen because the file is already in new/, and it will then use a
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen different filename. There's a few problems with this however:
0a49b316fc729e5d57268ffa63c7122ac73f994cTimo Sirainen - it requires extra stat() call which is unneeded extra I/O
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen - another MUA might still move the mail to cur/
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen - if first file's flags are modified by either Dovecot or another
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen MUA, it's moved to cur/ (you _could_ just do the dirty-flagging
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen but that'd be ugly)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen Because this is useful only for very few people and it requires
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen extra I/O, I decided not to implement this. It should be however
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen quite easy to do since we need to be able to deal with files in new/
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen It's also possible to never accidentally overwrite a mail by using
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen link() + unlink() rather than rename(). This however isn't very
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen good idea as it introduces potential race conditions when multiple
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen clients are accessing the mailbox:
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen Trying to move the same mail from new/ to cur/ at the same time:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen a) Client 1 uses slightly different filename than client 2,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen for example one sets read-flag on but the other doesn't.
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen You have the same mail duplicated now.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen b) Client 3 sees the mail between Client 1's and 2's link() calls
a2738cdb6d2733fb3e186331d68009421a19ea00Timo Sirainen and changes it's flag. You have the same mail duplicated now.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen And it gets worse when they're unlink()ing in cur/ directory:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen c) Client 1 changes mails's flag and client 2 changes it back
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen between 1's link() and unlink(). The mail is now expunged.
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen d) If you try to deal with the duplicates by unlink()ing another
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen one of them, you might end up unlinking both of them.
0892446b45c195461bb7be6599f02d97e1e2c9b2Timo Sirainen So, what should we do then if we notice a duplicate? First of all,
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen it might not be a duplicate at all, readdir() might have just
7af4788b402346c94496095dd819f95ce03fe431Timo Sirainen returned it twice because it was just renamed. What we should do is
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen create a completely new base name for it and rename() it to that.
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen If the call fails with ENOENT, it only means that it wasn't a
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainen duplicate after all.
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen/* When rename()ing many files from new/ to cur/, it's possible that next
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen readdir() skips some files. we don't of course wish to lose them, so we
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen go and rescan the new/ directory again from beginning until no files are
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen left. This value is just an optimization to avoid checking the directory
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen twice unneededly. usually only NFS is the problem case. 1 is the safest
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen bet here, but I guess 5 will do just fine too. */
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen struct maildir_uidlist_sync_ctx *uidlist_sync_ctx;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen struct maildir_index_sync_context *index_sync_ctx;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen struct maildir_keywords_sync_ctx *keywords_sync_ctx;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen ARRAY_DEFINE(sync_recs, struct mail_index_sync_rec);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainenmaildir_sync_get_keywords_sync_ctx(struct maildir_index_sync_context *ctx)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainenint maildir_filename_get_flags(struct maildir_keywords_sync_ctx *ctx,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (info == NULL || info[1] != '2' || info[2] != MAILDIR_FLAGS_SEP)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen for (info += 3; *info != '\0' && *info != MAILDIR_FLAGS_SEP; info++) {
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen /* unknown keyword. */
601f5f14c6cde28f0e0c6ca7c5d735315d3d48dfTimo Sirainen /* unknown flag - ignore */
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainenmaildir_filename_append_keywords(struct maildir_keywords_sync_ctx *ctx,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen const unsigned int *indexes;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen unsigned int i, count;
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen for (i = 0; i < count; i++) {
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen chr = maildir_keywords_idx_char(ctx, indexes[i]);
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenconst char *maildir_filename_set_flags(struct maildir_keywords_sync_ctx *ctx,
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen /* remove the old :info from file name, and get the old flags */
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen if (info != NULL && strrchr(fname, '/') > info)
548e394330621952db0f03dd667b70184c4a37b6Timo Sirainen if (info[1] == '2' && info[2] == MAILDIR_FLAGS_SEP)
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen /* insert the new flags between old flags. flags must be sorted by
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen their ASCII code. unknown flags are kept. */
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen str_append(flags_str, MAILDIR_FLAGS_FULL_SEP);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen /* skip all known flags */
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen while (*oldflags == 'D' || *oldflags == 'F' ||
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen nextflag = *oldflags == '\0' || *oldflags == MAILDIR_FLAGS_SEP ?
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen if ((flags_left & MAIL_DRAFT) && nextflag > 'D') {
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen if ((flags_left & MAIL_FLAGGED) && nextflag > 'F') {
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen if ((flags_left & MAIL_ANSWERED) && nextflag > 'R') {
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen if ((flags_left & MAIL_SEEN) && nextflag > 'S') {
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if ((flags_left & MAIL_DELETED) && nextflag > 'T') {
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen if (keywords != NULL && array_is_created(keywords) &&
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen maildir_filename_append_keywords(ctx, keywords,
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (*oldflags == '\0' || *oldflags == MAILDIR_FLAGS_SEP)
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen /* another flagset, we don't know about these, just keep them */
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainenstatic int maildir_expunge(struct maildir_mailbox *mbox, const char *path,
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen mail_storage_set_critical(STORAGE(mbox->storage),
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainenstatic int maildir_sync_flags(struct maildir_mailbox *mbox, const char *path,
df16c7e87511fed827e6890a2a47d13ca48716deTimo Sirainen unsigned int i, count;
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen (void)maildir_filename_get_flags(ctx->keywords_sync_ctx,
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen recs = array_get_modifiable(&ctx->sync_recs, &count);
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen for (i = 0; i < count; i++) {
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen mail_index_sync_flags_apply(&recs[i], &flags8);
c0b1543512bc3e0a3a9f526056a3678a07ce32f5Timo Sirainen mail_index_sync_keywords_apply(&recs[i], &keywords);
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen newfname = maildir_filename_set_flags(ctx->keywords_sync_ctx,
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen if ((flags8 & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_ADD,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen mail_storage_set_critical(STORAGE(mbox->storage),
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainenmaildir_sync_record_commit_until(struct maildir_index_sync_context *ctx,
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen unsigned int i, count;
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen recs = array_get_modifiable(&ctx->sync_recs, &count);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen for (seq = recs[0].uid1; seq <= last_seq; seq++) {
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen for (i = 0; i < count; i++) {
0b47e9f5e0181053b4d9ca7b426b0e5c185e820eTimo Sirainen if (mail_index_lookup_uid(ctx->view, seq, &uid) < 0) {
0b47e9f5e0181053b4d9ca7b426b0e5c185e820eTimo Sirainen mail_storage_set_index_error(&ctx->mbox->ibox);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen for (i = count; i > 0; i--) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* all sync_recs committed */
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainenstatic int maildir_sync_record(struct maildir_index_sync_context *ctx,
97e62b2b36dda0acb3215667042f5c80cdee8155Timo Sirainen if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_APPEND)
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen return 0; /* ignore */
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen /* convert to sequences to avoid looping through huge holes in
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen if (mail_index_lookup_uid_range(view, sync_rec->uid1,
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen mail_storage_set_index_error(&ctx->mbox->ibox);
14175321ddb88619015866978c05a27786ca4814Timo Sirainen /* UIDs were expunged */
14175321ddb88619015866978c05a27786ca4814Timo Sirainen if (maildir_sync_record_commit_until(ctx, sync_copy.uid1-1) < 0)
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainenstatic int maildir_sync_index_records(struct maildir_index_sync_context *ctx)
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen ret = mail_index_sync_next(ctx->sync_ctx, &sync_rec);
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen mail_storage_set_index_error(&ctx->mbox->ibox);
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen ret = mail_index_sync_next(ctx->sync_ctx, &sync_rec);
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen } while (ret > 0);
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen mail_storage_set_index_error(&ctx->mbox->ibox);
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainenmaildir_sync_context_new(struct maildir_mailbox *mbox)
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen ctx->new_dir = t_strconcat(mbox->path, "/new", NULL);
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen ctx->cur_dir = t_strconcat(mbox->path, "/cur", NULL);
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainenstatic void maildir_sync_deinit(struct maildir_sync_context *ctx)
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen (void)maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx);
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen (void)maildir_sync_index_finish(&ctx->index_sync_ctx,
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainenstatic int maildir_fix_duplicate(struct maildir_sync_context *ctx,
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen maildir_uidlist_sync_get_full_filename(ctx->uidlist_sync_ctx,
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen existing_path = t_strconcat(dir, "/", existing_fname, NULL);
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen old_path = t_strconcat(dir, "/", old_fname, NULL);
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen /* most likely the files just don't exist anymore.
4c096615cb86a826fda377b87df22c579bfe5525Timo Sirainen don't really care about other errors much. */
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if (st.st_ino == st2.st_ino && CMP_DEV_T(st.st_dev, st2.st_dev)) {
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen /* files are the same. this means either a race condition
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen between stat() calls, or someone has started link()ing the
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen files. either way there's no data loss if we just leave it
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen new_fname = maildir_generate_tmp_filename(&ioloop_timeval);
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen new_path = t_strconcat(mbox->path, "/new/", new_fname, NULL);
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen mail_storage_set_critical(STORAGE(mbox->storage),
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen "rename(%s, %s) failed: %m", old_path, new_path);
4aa7fe81503a20bc972ae625da4dd9e6996fbdbfTimo Sirainenstatic int maildir_scan_dir(struct maildir_sync_context *ctx, bool new_dir)
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen struct mail_storage *storage = STORAGE(ctx->mbox->storage);
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen const char *dir;
032964c7cc6788188b63ae6270fc26cbd4a3ca26Timo Sirainen unsigned int moves = 0;
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen move_new = new_dir && !mailbox_is_readonly(&ctx->mbox->ibox.box) &&
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen ret = maildir_uidlist_sync_next_pre(ctx->uidlist_sync_ctx,
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen /* new file and we couldn't lock uidlist, check this
12a3540693ab69ec622e04d1b3b66962b8b2a3d9Timo Sirainen later in next sync. */
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen str_printfa(src, "%s/%s", ctx->new_dir, dp->d_name);
9db5ade1c16c7f67c51004f28c28ea335755d3f0Timo Sirainen str_printfa(dest, "%s/%s", ctx->cur_dir, dp->d_name);
9db5ade1c16c7f67c51004f28c28ea335755d3f0Timo Sirainen if (strchr(dp->d_name, MAILDIR_INFO_SEP) == NULL) {
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen /* we moved it - it's \Recent for us */
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen /* someone else moved it already */
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen } else if (ENOSPACE(errno) || errno == EACCES) {
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen /* not enough disk space / read-only maildir,
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen leave here */
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen "rename(%s, %s) failed: %m",
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen } else if (new_dir) {
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen ret = maildir_uidlist_sync_next(ctx->uidlist_sync_ctx,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* possibly duplicate - try fixing it */
5f3151744f3ffa73b57391d4a237884b75423f57Timo Sirainen if (maildir_fix_duplicate(ctx, dir, dp->d_name) < 0) {
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen return ret < 0 ? -1 : (moves <= MAILDIR_RENAME_RESCAN_COUNT ? 0 : 1);
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenmaildir_sync_update_from_header(struct maildir_mailbox *mbox,
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen /* open a new view so we get the latest header */
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen view = mail_index_view_open(mbox->ibox.index);
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen /* FIXME: ugly, replace with extension header */
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen mbox->last_new_mtime = hdr->sync_size & 0xffffffff;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen mbox->last_dirty_flags = (hdr->sync_size >> 32) &
0c1835a90dd1dcedaeaedd1cd91672299cbeb5beTimo Sirainen if ((mbox->last_dirty_flags & MAILDIR_DIRTY_CUR) != 0 &&
d14e62b7b37dc78fcc940aca25042eceb358b156Timo Sirainenmaildir_sync_quick_check(struct maildir_mailbox *mbox,
f4735bf7ec2019fdc730e9ebdb39e5a4ea580405Timo Sirainen mail_storage_set_critical(STORAGE(mbox->storage),
} else if (seq != 0) {
return ret;
bool partial)
const char *filename;
int ret = 0;
seq = 0;
seq++;
if ((uflags &
MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) {
if ((uflags &
MAILDIR_UIDLIST_REC_FLAG_RACING) != 0) {
filename);
seq--;
flags);
goto __again;
if ((uflags &
MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) {
seq--;
seq--;
flags);
if (!partial) {
if (uid_validity == 0) {
} else if (uid_validity == 0) {
bool sync_last_commit)
int ret;
if (sync_last_commit) {
} else if (!forced) {
if (ret <= 0) {
return ret;
if (ret < 0)
if (cur_changed) {
if (ret < 0)
if (ret == 0)
int ret;
int ret;
struct mailbox_sync_context *
int ret = 0;
ioloop_time) {
if (ret == 0) {
int ret;
t_push();
t_pop();