This is going to help with race conditions when deleting mailboxes.
--HG--
branch : HEAD
case MAIL_TRANSACTION_MODSEQ_UPDATE:
name = "modseq-update";
break;
+ case MAIL_TRANSACTION_INDEX_DELETED:
+ name = "index-deleted";
+ break;
default:
name = t_strdup_printf("unknown: %x", type);
break;
}
break;
}
+ case MAIL_TRANSACTION_INDEX_DELETED:
+ break;
default:
break;
}
unsigned int index_lock_timeout:1;
unsigned int opened:1;
+ unsigned int index_deleted:1; /* no changes allowed anymore */
unsigned int log_locked:1;
unsigned int readonly:1;
unsigned int mapping:1;
ret = sync_modseq_update(ctx, rec, hdr->size);
break;
}
+ case MAIL_TRANSACTION_INDEX_DELETED:
+ if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0)
+ break;
+ /* transaction log syncing should have already set this */
+ i_assert(ctx->view->index->index_deleted);
+ break;
default:
mail_index_sync_set_corrupted(ctx,
"Unknown transaction record type 0x%x",
MAIL_TRANSACTION_HEADER_UPDATE);
}
+ if (t->index_deleted) {
+ static uint8_t null4[4] = { 0, 0, 0, 0 };
+ mail_transaction_log_append_add(ctx.append_ctx,
+ MAIL_TRANSACTION_INDEX_DELETED,
+ &null4, 4);
+ }
+
/* Update the tail offsets only when committing the sync transaction.
Other transactions may not know the latest tail offset and might
end up shrinking it. (Alternatively the shrinking tail offsets could
unsigned int pre_hdr_changed:1;
unsigned int post_hdr_changed:1;
unsigned int reset:1;
+ unsigned int index_deleted:1;
/* non-extension updates. flag updates don't change this because
they may be added and removed, so be sure to check that the updates
array is non-empty also. */
t->pre_hdr_changed = FALSE;
t->post_hdr_changed = FALSE;
t->reset = FALSE;
+ t->index_deleted = FALSE;
t->log_updates = FALSE;
t->log_ext_updates = FALSE;
}
t->reset = TRUE;
}
+void mail_index_set_deleted(struct mail_index_transaction *t)
+{
+ i_assert((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0);
+
+ t->index_deleted = TRUE;
+}
+
void mail_index_transaction_set_max_modseq(struct mail_index_transaction *t,
uint64_t max_modseq,
ARRAY_TYPE(seq_range) *seqs)
mail_index_transaction_rollback(_t);
return -1;
}
+ if (t->view->index->index_deleted) {
+ /* no further changes allowed */
+ mail_index_transaction_rollback(_t);
+ return -1;
+ }
*_t = NULL;
memset(result_r, 0, sizeof(*result_r));
}
}
+bool mail_index_is_deleted(struct mail_index *index)
+{
+ return index->index_deleted;
+}
+
void mail_index_fchown(struct mail_index *index, int fd, const char *path)
{
mode_t mode;
/* Reset the index before committing this transaction. This is usually done
only when UIDVALIDITY changes. */
void mail_index_reset(struct mail_index_transaction *t);
+/* Mark index deleted. No further changes will be possible after the
+ transaction has been committed. */
+void mail_index_set_deleted(struct mail_index_transaction *t);
+/* Returns TRUE if index has been set deleted. This gets set only after
+ index has been opened/refreshed and the transaction has been seen. */
+bool mail_index_is_deleted(struct mail_index *index);
/* Lookup a keyword, returns TRUE if found, FALSE if not. */
bool mail_index_keyword_lookup(struct mail_index *index,
file->log = log;
file->filepath = i_strdup(path);
file->fd = -1;
+ file->index_deleted_offset = (uoff_t)-1;
return file;
}
return 0;
/* external transactions: */
- if ((hdr->type & MAIL_TRANSACTION_TYPE_MASK) ==
- MAIL_TRANSACTION_HEADER_UPDATE) {
+ switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
+ case MAIL_TRANSACTION_HEADER_UPDATE:
/* see if this updates mailbox_sync_offset */
ret = log_file_track_mailbox_sync_offset_hdr(file, data,
trans_size -
sizeof(*hdr));
if (ret != 0)
return ret < 0 ? -1 : 0;
+ break;
+ case MAIL_TRANSACTION_INDEX_DELETED:
+ file->log->index->index_deleted = TRUE;
+ file->index_deleted_offset = file->sync_offset + trans_size;
+ break;
}
if (file->max_tail_offset == file->sync_offset) {
data = buffer_get_data(file->buffer, &size);
while (file->sync_offset - file->buffer_offset + sizeof(*hdr) <= size) {
+ if (unlikely(file->index_deleted_offset == file->sync_offset)) {
+ /* ignore everything that comes after _INDEX_DELETED */
+ break;
+ }
hdr = CONST_PTR_OFFSET(data, file->sync_offset -
file->buffer_offset);
trans_size = mail_index_offset_to_uint32(hdr->size);
sync_offset is less than this. */
uoff_t saved_tail_sync_offset;
+ /* if we've seen _INDEX_DELETED transaction in this file, this is the
+ offset. otherwise (uoff_t)-1 */
+ uoff_t index_deleted_offset;
+
struct modseq_cache modseq_cache[LOG_FILE_MODSEQ_CACHE_SIZE];
struct file_lock *file_lock;
MAIL_TRANSACTION_UID_UPDATE = 0x00004000,
MAIL_TRANSACTION_MODSEQ_UPDATE = 0x00008000,
MAIL_TRANSACTION_EXT_HDR_UPDATE32 = 0x00010000,
+ MAIL_TRANSACTION_INDEX_DELETED = 0x00020000,
MAIL_TRANSACTION_TYPE_MASK = 0x000fffff,
enum mail_flags save_flags;
struct istream *crlf_input;
+ if (mail_index_is_deleted(ctx->mbox->ibox.index)) {
+ mailbox_set_deleted(trans->box);
+ return -1;
+ }
+
T_BEGIN {
const char *path;
struct dbox_save_mail *save_mail;
uoff_t mail_size, append_offset;
+ if (mail_index_is_deleted(ctx->mbox->ibox.index)) {
+ mailbox_set_deleted(_ctx->transaction->box);
+ return -1;
+ }
+
/* get the size of the mail to be saved, if possible */
if (i_stream_get_size(input, TRUE, &mail_size) <= 0) {
const struct stat *st;
struct dbox_file *file;
int ret;
+ if (mail_index_is_deleted(ctx->mbox->ibox.index)) {
+ mailbox_set_deleted(_ctx->transaction->box);
+ return -1;
+ }
+
file = sdbox_file_init(ctx->mbox, 0);
ctx->append_ctx = dbox_file_append_init(file);
ret = dbox_file_get_append_stream(ctx->append_ctx,
index_thread_mailbox_opened(ibox);
if (hook_mailbox_opened != NULL)
hook_mailbox_opened(box);
+
+ if (mail_index_is_deleted(ibox->index)) {
+ mailbox_set_deleted(box);
+ return -1;
+ }
return 0;
}
void mail_storage_set_index_error(struct index_mailbox *ibox)
{
- mail_storage_set_internal_error(ibox->box.storage);
+ if (mail_index_is_deleted(ibox->index))
+ mailbox_set_deleted(&ibox->box);
+ else
+ mail_storage_set_internal_error(ibox->box.storage);
mail_index_reset_error(ibox->index);
}
_t->changes = changes_r;
ret = mail_index_transaction_commit_full(&itrans, &result);
+ if (ret < 0 && mail_index_is_deleted(ibox->index))
+ mailbox_set_deleted(&ibox->box);
changes_r->ignored_uid_changes = result.ignored_uid_changes;
changes_r->ignored_modseq_changes = result.ignored_modseq_changes;
return 0;
}
+ if (mail_index_is_deleted(dest_mbox->ibox.index)) {
+ mailbox_set_deleted(&dest_mbox->ibox.box);
+ return -1;
+ }
+
memset(&do_ctx, 0, sizeof(do_ctx));
do_ctx.dest_path = str_new(default_pool, 512);
string_t *path;
int fd;
+ if (mail_index_is_deleted(mbox->ibox.index)) {
+ mailbox_set_deleted(box);
+ return -1;
+ }
+
path = t_str_new(256);
str_append(path, dir);
str_append_c(path, '/');
return -1;
}
+ if (mail_index_is_deleted(mbox->ibox.index)) {
+ mailbox_set_deleted(&mbox->ibox.box);
+ return -1;
+ }
+
if ((_t->flags & MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS) != 0 ||
ctx->ctx.uid != 0)
want_mail = TRUE;