maildir-sync-index.c revision 389e0ad41d4dd6acf50f855ecd5f0651082c9f31
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2007-2008 Dovecot authors, see the included COPYING file */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen struct maildir_sync_context *maildir_sync_ctx;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen struct maildir_keywords_sync_ctx *keywords_sync_ctx;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct maildir_uidlist_sync_ctx *uidlist_sync_ctx;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct index_sync_changes_context *sync_changes;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmaildir_sync_get_keywords_sync_ctx(struct maildir_index_sync_context *ctx)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int maildir_expunge(struct maildir_mailbox *mbox, const char *path,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_storage_set_critical(&mbox->storage->storage,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int maildir_sync_flags(struct maildir_mailbox *mbox, const char *path,
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen /* get the current flags and keywords */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen maildir_filename_get_flags(ctx->keywords_sync_ctx,
ec1e30ecc38f0deddaf655413cf02d5972ddbc70Timo Sirainen /* apply changes */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen index_sync_changes_apply(ctx->sync_changes, NULL,
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen /* and try renaming with the new name */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen newfname = maildir_filename_set_flags(ctx->keywords_sync_ctx, fname,
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainenstatic void maildir_handle_uid_insertion(struct maildir_index_sync_context *ctx,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((uflags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* partial syncing */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* most likely a race condition: we read the maildir, then someone else
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen expunged messages and committed changes to index. so, this message
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen shouldn't actually exist. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((uflags & MAILDIR_UIDLIST_REC_FLAG_RACING) == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* mark it racy and check in next sync */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen maildir_uidlist_add_flags(ctx->mbox->uidlist, filename,
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen ret = maildir_uidlist_sync_init(ctx->mbox->uidlist,
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen maildir_uidlist_sync_remove(ctx->uidlist_sync_ctx, filename);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen ret = maildir_uidlist_sync_next(ctx->uidlist_sync_ctx,
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen /* give the new UID to it immediately */
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen maildir_uidlist_sync_finish(ctx->uidlist_sync_ctx);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen i_warning("Maildir %s: Expunged message reappeared, giving a new UID "
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen "(old uid=%u, file=%s)", ctx->mbox->path, uid, filename);
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainenint maildir_sync_index_begin(struct maildir_mailbox *mbox,
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen struct maildir_sync_context *maildir_sync_ctx,
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen /* don't drop recent messages if we're saving messages */
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen if (!mbox->ibox.keep_recent && maildir_sync_ctx != NULL)
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen sync_flags |= MAIL_INDEX_SYNC_FLAG_DROP_RECENT;
4c8b1c4aa0582c6ca43a4d1cbd210741e7fff952Timo Sirainen if (mail_index_sync_begin(mbox->ibox.index, &sync_ctx, &view, &trans,
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen ctx = i_new(struct maildir_index_sync_context, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen maildir_keywords_sync_init(mbox->keywords, mbox->ibox.index);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen ctx->sync_changes = index_sync_changes_init(&mbox->ibox, ctx->sync_ctx,
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainenint maildir_sync_index_finish(struct maildir_index_sync_context **_ctx,
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen struct maildir_index_sync_context *ctx = *_ctx;
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen /* Set syncing_commit=TRUE so that if any sync callbacks try
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen to access mails which got lost (eg. expunge callback trying
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen to open the file which was just unlinked) we don't try to
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen start a second index sync and crash. */
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen if (mail_index_sync_commit(&ctx->sync_ctx) < 0) {
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen maildir_keywords_sync_deinit(&ctx->keywords_sync_ctx);
5529671faac3c5672a948be93091056736c7afffTimo Sirainen index_sync_changes_deinit(&ctx->sync_changes);
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainenmaildir_index_header_has_changed(const struct maildir_index_header *old_hdr,
8afec4d1a32b78f540257a27769b372aad753384Timo Sirainen if (old_hdr->new_mtime != new_hdr->new_mtime ||
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen old_hdr->new_mtime_nsecs != new_hdr->new_mtime_nsecs ||
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen old_hdr->cur_mtime_nsecs != new_hdr->cur_mtime_nsecs)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmaildir_index_update_ext_header(struct maildir_mailbox *mbox,
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen mail_index_get_header_ext(mbox->ibox.view, mbox->maildir_ext_id,
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen maildir_index_header_has_changed(data, &mbox->maildir_hdr)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_update_header_ext(trans, mbox->maildir_ext_id, 0,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint maildir_sync_index(struct maildir_index_sync_context *ctx,
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen struct mail_index_transaction *trans = ctx->trans;
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen uint32_t uid_validity, next_uid, hdr_next_uid, first_recent_uid;
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen unsigned int changes = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(maildir_uidlist_is_locked(mbox->uidlist));
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen uid_validity = maildir_uidlist_get_uid_validity(mbox->uidlist);
62950eeff28f00989a17b20eeade3af7e200c6bcTimo Sirainen uid_validity != 0 && hdr->uid_validity != 0) {
62950eeff28f00989a17b20eeade3af7e200c6bcTimo Sirainen /* uidvalidity changed and index isn't being synced for the
62950eeff28f00989a17b20eeade3af7e200c6bcTimo Sirainen first time, reset the index so we can add all messages as
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen i_warning("Maildir %s: UIDVALIDITY changed (%u -> %u)",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen maildir_uidlist_set_next_uid(mbox->uidlist, 1, TRUE);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen seq = prev_uid = 0; first_recent_uid = hdr->first_recent_uid;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_array_init(&ctx->keywords, MAILDIR_MAX_KEYWORDS);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_array_init(&idx_keywords, MAILDIR_MAX_KEYWORDS);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen iter = maildir_uidlist_iter_init(mbox->uidlist);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen while (maildir_uidlist_iter_next(iter, &uid, &uflags, &filename)) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen maildir_filename_get_flags(ctx->keywords_sync_ctx, filename,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* the private flags are kept only in indexes. don't use them
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen at all even for newly seen mails */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ctx->flags &= ~mbox->ibox.box.private_flags_mask;
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen /* Trust uidlist recent flags only for newly added
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen messages. When saving/copying messages with flags
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen they're stored to cur/ and uidlist treats them
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen as non-recent. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((uflags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_update_flags(trans, seq, MODIFY_REPLACE,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* expunged */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen index_sync_changes_read(ctx->sync_changes, rec->uid, &expunged);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* successful expunge */
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen if ((++changes % MAILDIR_SLOW_MOVE_COUNT) == 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* the private flags are stored only in indexes, keep them */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ctx->flags |= rec->flags & mbox->ibox.box.private_flags_mask;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (index_sync_changes_have(ctx->sync_changes)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* apply flag changes to maildir */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if ((++changes % MAILDIR_SLOW_MOVE_COUNT) == 0)
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen if ((uflags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) {
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen /* partial syncing */
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen if ((uflags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0) {
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen /* we last saw this mail in new/, but it's
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen not there anymore. possibly expunged,
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen make sure. */
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen if ((rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) {
287ba82a8da3eaa473b5735d4eeac2fb4c5d8117Timo Sirainen /* we haven't been able to update maildir with this
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen record's flag changes. don't sync them. */
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen if (ctx->flags != (rec->flags & MAIL_FLAGS_NONRECENT)) {
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen mail_index_update_flags(trans, seq, MODIFY_REPLACE,
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen /* update keywords if they have changed */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen mail_index_lookup_keywords(view, seq, &idx_keywords);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (!index_keyword_array_cmp(&ctx->keywords, &idx_keywords)) {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen /* expunge the rest */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen for (seq++; seq <= hdr->messages_count; seq++)
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen /* add \Recent flags. use updated view so it contains newly
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen appended messages. */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen view2 = mail_index_transaction_open_updated_view(trans);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (mail_index_lookup_seq_range(view2, first_recent_uid, (uint32_t)-1,
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen index_mailbox_set_recent_seq(&mbox->ibox, view2, seq, seq2);
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen if (maildir_uidlist_sync_deinit(&ctx->uidlist_sync_ctx) < 0)
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen mbox->ibox.box.v.sync_notify(&mbox->ibox.box, 0, 0);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen maildir_uidlist_set_uid_validity(mbox->uidlist, uid_validity);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen maildir_uidlist_set_next_uid(mbox->uidlist, hdr_next_uid, FALSE);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen offsetof(struct mail_index_header, uid_validity),
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen next_uid = maildir_uidlist_get_next_uid(mbox->uidlist);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen i_assert(hdr->first_recent_uid <= first_recent_uid);
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen if (hdr->first_recent_uid < first_recent_uid) {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen offsetof(struct mail_index_header, first_recent_uid),
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen &first_recent_uid, sizeof(first_recent_uid), FALSE);
bb2b91b4c5363348b737237893d414639510a561Timo Sirainenstatic unsigned int maildir_list_get_ext_id(struct maildir_storage *storage,
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen if (storage->maildir_list_ext_id == (uint32_t)-1) {
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen mail_index_ext_register(mail_index_view_get_index(view),
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen "maildir", 0,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainenint maildir_list_index_has_changed(struct mailbox *box,
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen ext_id = maildir_list_get_ext_id(mbox->storage, list_view);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen mail_index_lookup_ext(list_view, seq, ext_id, &data, &expunged);
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen /* doesn't exist, not synced or dirty-synced */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen root_dir = mailbox_list_get_path(mail_storage_get_list(box->storage),
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen /* check if new/ changed */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen new_dir = t_strconcat(root_dir, "/new", NULL);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen /* check if cur/ changed */
bb2b91b4c5363348b737237893d414639510a561Timo Sirainen cur_dir = t_strconcat(root_dir, "/cur", NULL);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenint maildir_list_index_update_sync(struct mailbox *box,
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)box;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen const struct maildir_index_header *mhdr = &mbox->maildir_hdr;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen const struct maildir_list_index_record *old_rec;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* get the current record */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen list_view = mail_index_transaction_get_view(trans);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen ext_id = maildir_list_get_ext_id(mbox->storage, list_view);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen mail_index_lookup_ext(list_view, seq, ext_id, &data, &expunged);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen if (mhdr->new_check_time <= mhdr->new_mtime + MAILDIR_SYNC_SECS ||
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen mhdr->cur_check_time <= mhdr->cur_mtime + MAILDIR_SYNC_SECS) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* dirty, we need a refresh next time */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen memcmp(old_rec, &new_rec, sizeof(old_rec)) != 0) {