Mailbox deletion race: folders and files are never deleted
Samir Aguiar
samir.aguiar at intra2net.com
Thu Feb 26 10:51:53 EST 2015
Hi,
we found a race condition problem in Cyrus IMAP 2.4.17 where mailbox folders
(and the files inside them) are not removed when deleting a mailbox with delete
mode set to "immediate".
The steps to reproduce are:
- Open a mailbox in one session (session A);
- Delete that mailbox with another session (session B).
Session B updates the index with the OPT_MAILBOX_DELETED flag and removes the
mailbox from the database. It expects session A to wipe out the files from the
filesystem later in mailbox_close().
Because the database now has no record of the mailbox, no further sessions
will be able to clean up those files. Neither will cyr_expire, because that
mailbox won't be listed by the mboxlist* functions. So session A is the one
that should remove the leftovers.
However, when closing the mailbox, session A executes the following piece of
code:
/* drop the index lock here because we'll lose our right to it
* when try to upgrade the mboxlock anyway. */
mailbox_unlock_index(mailbox, NULL);
/* do we need to try and clean up? (not if doing a shutdown,
* speed is probably more important!) */
if (!in_shutdown && (mailbox->i.options & MAILBOX_CLEANUP_MASK)) {
int r = mailbox_mboxlock_reopen(listitem, LOCK_NONBLOCKING);
if (!r) r = mailbox_open_index(mailbox);
if (!r) r = mailbox_lock_index(mailbox, LOCK_EXCLUSIVE);
if (!r) {
/* finish cleaning up */
if (mailbox->i.options & OPT_MAILBOX_DELETED)
mailbox_delete_cleanup(mailbox->part, mailbox->name);
else if (mailbox->i.options & OPT_MAILBOX_NEEDS_REPACK)
mailbox_index_repack(mailbox);
else if (mailbox->i.options & OPT_MAILBOX_NEEDS_UNLINK)
mailbox_index_unlink(mailbox);
/* or we missed out - someone else beat us to it */
}
/* otherwise someone else has the mailbox locked
* already, so they can handle the cleanup in
* THEIR mailbox_close call */
}
There are two problems here:
- if the "in_shutdown" flag is set, session A will exit and the files will be
forgotten forever;
- the index file is only reloaded by the call to mailbox_open_index, which is
inside the if. That means that session A will be unaware of the clean up mask
set by session B when checking this if and again the files will never be
cleaned.
Proposed solution:
- Patch the mailbox_close() function to reload the index before trying to
clean the files (and merge the current index changes with the ones found)
- Because the above would still be skipped when shutting down Cyrus: expand
cyr_expire to find mailboxes with the deleted flag set (by looping through the
filesystem, and not by using the database) and try to remove them. This could
then be run periodically by administrators.
What do you think?
Best regards,
Samir Aguiar
More information about the Cyrus-devel
mailing list