dbox-sync-expunge.c revision e063aca6bc2f08bec516d4b631052ea9191f011d
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher/* Copyright (C) 2005 Timo Sirainen */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherdbox_sync_rec_get_uids(struct dbox_sync_context *ctx,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (mail_index_lookup_uid(ctx->sync_view, sync_rec->seq1, uid1_r) < 0) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher mail_storage_set_index_error(&ctx->mbox->ibox);
74e95cfd9d3939dfe9417d79d2f6fc79b361405fJakub Hrozek if (mail_index_lookup_uid(ctx->sync_view, sync_rec->seq2, uid2_r) < 0) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher mail_storage_set_index_error(&ctx->mbox->ibox);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherdbox_next_expunge(struct dbox_sync_context *ctx,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher const struct dbox_sync_file_entry *sync_entry,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher unsigned int *sync_idx, uint32_t *uid1_r, uint32_t *uid2_r)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct mailbox *box = &ctx->mbox->ibox.box;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher const struct dbox_sync_rec *sync_recs, *sync_rec;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher sync_recs = array_get(&sync_entry->sync_recs, &count);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (sync_rec->type != MAIL_INDEX_SYNC_TYPE_EXPUNGE)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (dbox_sync_rec_get_uids(ctx, sync_rec, uid1_r, uid2_r) < 0)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* all of the UIDs uid1..uid2 should exist */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (uid = *uid1_r; uid <= *uid2_r; uid++) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (seq = sync_rec->seq1; seq != sync_rec->seq2; seq++)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic int dbox_sync_expunge_copy(struct dbox_sync_context *ctx,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher const struct dbox_sync_file_entry *sync_entry,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher const struct dbox_uidlist_entry *orig_entry,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct mail_storage *storage = &mbox->storage->storage;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret = dbox_file_seek(mbox, orig_entry->file_seq, orig_offset, FALSE);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (ret >= 0 && mbox->file->hdr.have_expunged_mails != '0') {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* there are some expunged mails in the file, go through all
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher of the mails. */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret = dbox_file_seek(mbox, orig_entry->file_seq,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* skip mails until we find the first we don't want expunged */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher while (ret > 0) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (mbox->file->seeked_uid >= first_nonexpunged_uid)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher "%s: Expunging lost UID %u from file %u",
346f41f1ede975cb2db0af570f5b454b9b306704Stephen Gallagher sync_recs = array_get(&sync_entry->sync_recs, &sync_count);
346f41f1ede975cb2db0af570f5b454b9b306704Stephen Gallagher if (dbox_sync_rec_get_uids(ctx, &sync_recs[sync_idx],
346f41f1ede975cb2db0af570f5b454b9b306704Stephen Gallagher file_seq = dbox_uidlist_get_new_file_seq(mbox->uidlist);
346f41f1ede975cb2db0af570f5b454b9b306704Stephen Gallagher path = t_strdup_printf("%s/"DBOX_MAIL_FILE_FORMAT,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher fd = file_dotlock_open(&mbox->storage->new_file_dotlock_set,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher "file_dotlock_open(%s) failed: %m", path);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* try again with another file name */
7119f0c483049a8850d3075c0b1062f35200a538Jakub Hrozek output = o_stream_create_file(fd, default_pool, 0, FALSE);
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek lock_path = file_dotlock_get_lock_path(dotlock);
7119f0c483049a8850d3075c0b1062f35200a538Jakub Hrozek t_array_init(&dest_entry.uid_list, array_count(&orig_entry->uid_list));
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zeleny /* write file header */
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zeleny while (ret > 0) {
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zeleny /* update mail's location in index */
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zeleny if (mail_index_lookup_uid_range(ctx->sync_view, uid, uid,
4a1e58d85409fbb7a12ac244c3dbef8c0c1b15dfMichal Zidek "Expunged UID %u reappeared in file %s",
4a1e58d85409fbb7a12ac244c3dbef8c0c1b15dfMichal Zidek mail_index_update_ext(ctx->trans, seq, mbox->dbox_file_ext_idx,
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek /* copy the mail */
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek input = i_stream_create_limit(default_pool, mbox->file->input,
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek "o_stream_send_istream(%s) failed: %m",
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek "o_stream_send_istream(%s) wrote only %"
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zeleny /* seek to next non-expunged mail */
b096321a5a02dda0b6b71ba0f9c4d8feacd979e4Michal Zidek while (mbox->file->seeked_uid > uid2 && uid2 != 0) {
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek /* we want to keep copying */
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek if (mbox->file->seeked_uid < uid1 || uid1 == 0)
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek /* update append_offset in header */
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek DEC2HEX(hdr.append_offset_hex, output->offset);
7119f0c483049a8850d3075c0b1062f35200a538Jakub Hrozek /* new file created successfully. append it to uidlist. */
7119f0c483049a8850d3075c0b1062f35200a538Jakub Hrozek dbox_uidlist_sync_append(ctx->uidlist_sync_ctx, &dest_entry);
7119f0c483049a8850d3075c0b1062f35200a538Jakub Hrozekstatic int dbox_sync_expunge_file(struct dbox_sync_context *ctx,
7119f0c483049a8850d3075c0b1062f35200a538Jakub Hrozek unsigned int sync_idx)
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek const char *path;
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek uint32_t file_seq, uid, exp_uid1, exp_uid2, first_expunged_uid;
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek sync_recs = array_get(&sync_entry->sync_recs, &sync_count);
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek if (dbox_sync_get_file_offset(ctx, sync_recs[sync_idx].seq1,
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek entry = dbox_uidlist_entry_lookup(mbox->uidlist, sync_entry->file_seq);
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek /* file is already unlinked. just remove from index. */
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek if (dbox_sync_rec_get_uids(ctx, &sync_recs[sync_idx],
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek /* find the first non-expunged mail */
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek seen_expunges = FALSE; skipped_expunges = FALSE; uid = 0;
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek range = array_get_modifiable(&entry->uid_list, &count);
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek for (i = 0; i < count; i++) {
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zeleny /* range begins with non-expunged messages */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* non-expunged mails exist in this file */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* fully used up this uid range */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* this sync_rec was fully used. look up the next.
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek range[] doesn't contain non-existing UIDs, so
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek exp_uid2+1 should exist in it. */
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek ret = dbox_next_expunge(ctx, sync_entry, &sync_idx,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* end of sync records */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* non-expunged mails exist in this file */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* mails expunged from the middle. have to copy everything
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher after the first expunged mail to new file. after copying
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher we'll truncate/unlink the old file. */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (dbox_sync_expunge_copy(ctx, sync_entry, sync_idx,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* all mails expunged from file, unlink it. */
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek path = t_strdup_printf("%s/"DBOX_MAIL_FILE_FORMAT,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher mail_storage_set_critical(&mbox->storage->storage,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher dbox_uidlist_sync_unlink(ctx->uidlist_sync_ctx,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* mails expunged from the end of file, ftruncate() it */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret = dbox_file_seek(mbox, entry->file_seq, offset, FALSE);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* unexpected EOF -> already truncated */
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek /* file can no longer be appended to */
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek if (pwrite_full(mbox->file->fd, "00000000EFFFFFFF", 16,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher mail_storage_set_critical(&mbox->storage->storage,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher "pwrite_full(%s) failed: %m", mbox->path);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher mail_storage_set_critical(&mbox->storage->storage,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher "ftruncate(%s) failed: %m", mbox->file->path);
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek if (mbox->file->hdr.have_expunged_mails != '0') {
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek /* all mails in the file are expunged now */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher "pwrite_full(%s) failed: %m",
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* remove from uidlist entry */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (; i > 0; i--) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher array_delete(&entry->uid_list, i, count-i);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (i > 0 && range[i-1].seq2 >= first_expunged_uid)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* file can no longer be written to */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher dbox_uidlist_sync_set_modified(ctx->uidlist_sync_ctx);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagheruidlist_entry_remove_uids(struct dbox_sync_context *ctx,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher const struct dbox_sync_file_entry *sync_entry)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher entry = dbox_uidlist_entry_lookup(ctx->mbox->uidlist,
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek recs = array_get(&sync_entry->sync_recs, &count);
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek for (i = 0; i < count; i++) {
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek if (recs[i].type != MAIL_INDEX_SYNC_TYPE_EXPUNGE)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (seq = recs[i].seq1; seq <= recs[i].seq2; seq++) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher mail_storage_set_index_error(&ctx->mbox->ibox);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher seq_range_array_remove(&entry->uid_list, uid);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher dbox_uidlist_sync_unlink(ctx->uidlist_sync_ctx,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher dbox_uidlist_sync_set_modified(ctx->uidlist_sync_ctx);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherint dbox_sync_expunge(struct dbox_sync_context *ctx,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher const struct dbox_sync_file_entry *sync_entry,
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek if (ctx->dotlock_failed_file_seq != sync_entry->file_seq) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* we need to have the file locked in case another process is
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher appending there already. */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher path = t_strdup_printf("%s/"DBOX_MAIL_FILE_FORMAT,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret = file_dotlock_create(&mbox->storage->new_file_dotlock_set,
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek mail_storage_set_critical(&mbox->storage->storage,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* locked - copy the non-expunged mails after the
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher expunged mail to new file */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher ret = dbox_sync_expunge_file(ctx, sync_entry, sync_idx);
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek /* remember that we failed, so we don't waste time trying to
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek lock the file multiple times within same sync. */
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek ctx->dotlock_failed_file_seq = sync_entry->file_seq;
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek /* couldn't lock it, someone's appending. we have no other
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek choice but to just mark the mail expunged. otherwise we'd
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek deadlock (appending process waits for uidlist lock which
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek we have, we wait for file lock which append process has) */
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek sync_rec = array_idx(&sync_entry->sync_recs, sync_idx);
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek /* mark in the header that the file contains expunged messages */
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek mail_storage_set_critical(&mbox->storage->storage,
55d80b1301fe969fb4ba2b9481027887b9462dbbJakub Hrozek /* remove UIDs from the uidlist entry */