mail-index-lock.c revision 325d4ad220bd13f6d176391d962a0e33c856a7f6
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (C) 2003-2004 Timo Sirainen */
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen Locking should never fail or timeout. Exclusive locks must be kept as short
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen time as possible. Shared locks can be long living, so if we can't get
1171f0abf442638bac1827bb24a0b6b8eb682a82Timo Sirainen exclusive lock directly within 2 seconds, we'll replace the index file with
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen a copy of it. That means the shared lock holders can keep using the old file
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen while we're modifying the new file.
1d738cce754bc64bbc66d3355ebdaf3f6eac55f1Timo Sirainen lock_id is used to figure out if acquired lock is still valid. When index
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen file is reopened, the lock_id can become invalid. It doesn't matter however,
fcfb528483369975066c6adf1c55c16e6fb6e91fTimo Sirainen as no-one's going to modify the old file anymore.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen lock_id also tells if we're referring to shared or exclusive lock. This
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen allows us to drop back to shared locking once all exclusive locks are
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainen dropped. Shared locks have even numbers, exclusive locks have odd numbers.
9bd08aa09ea0cbd7b221aae9fc0534eb762d3de6Timo Sirainen The number is increased by two every time the lock is dropped or index file
82ed69779f49bd71ef1b570ce8aca67d357dbee8Timo Sirainenint mail_index_lock_fd(struct mail_index *index, const char *path, int fd,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = fcntl(fd, timeout_secs ? F_SETLKW : F_SETLK, &fl);
6a7f64562ddd0dd2fec755ec4e9c9afde8e85cf1Timo Sirainen /* locked by another process */
6a7f64562ddd0dd2fec755ec4e9c9afde8e85cf1Timo Sirainen /* most likely alarm hit, meaning we timeouted.
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen even if not, we probably want to be killed
6a7f64562ddd0dd2fec755ec4e9c9afde8e85cf1Timo Sirainen so stop blocking. */
6a7f64562ddd0dd2fec755ec4e9c9afde8e85cf1Timo Sirainen mail_index_file_set_syscall_error(index, path,
6a7f64562ddd0dd2fec755ec4e9c9afde8e85cf1Timo Sirainen "mail_index_lock_fd()");
6a7f64562ddd0dd2fec755ec4e9c9afde8e85cf1Timo Sirainen int operation = timeout_secs != 0 ? 0 : LOCK_NB;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen /* a) locked by another process,
6a7f64562ddd0dd2fec755ec4e9c9afde8e85cf1Timo Sirainen b) timeouted */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_file_set_syscall_error(index, path,
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen "mail_index_lock_fd()");
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen /* we shouldn't get here */
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainenstatic int mail_index_lock(struct mail_index *index, int lock_type,
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen unsigned int *lock_id_r)
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK);
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen if (lock_type == F_RDLCK && index->lock_type != F_UNLCK) {
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen } else if (lock_type == F_WRLCK && index->lock_type == F_WRLCK) {
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen if (update_index && index->excl_lock_count == 0) {
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen if ((ret2 = mail_index_reopen_if_needed(index)) < 0)
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen if (index->lock_method == MAIL_INDEX_LOCK_DOTLOCK) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* FIXME: exclusive locking will rewrite the index file every
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen time. shouldn't really be needed.. reading doesn't require
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch locks then, though */
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen if (update_index && index->lock_type == F_UNLCK) {
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen if (lock_type == F_RDLCK || !index->log_locked) {
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen ret = mail_index_lock_fd(index, index->filepath, index->fd,
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* this is kind of kludgy. we wish to avoid deadlocks while
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen trying to lock transaction log, but it can happen if our
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen process is holding transaction log lock and waiting for
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen index write lock, while the other process is holding index
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen read lock and waiting for transaction log lock.
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen we don't have a problem with grabbing read index lock
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen because the only way for it to block is if it's
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen write-locked, which isn't allowed unless transaction log
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen is also locked.
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen so, the workaround for this problem is that we simply try
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen locking once. if it doesn't work, just rewrite the file.
91496fd60a7980f4ebdf93cbb099b9db198a0e74Timo Sirainen hopefully there won't be any other deadlocking issues. :) */
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen ret = mail_index_lock_fd(index, index->filepath, index->fd,
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainenint mail_index_lock_shared(struct mail_index *index, int update_index,
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen unsigned int *lock_id_r)
d6693dac50e4fb547d8dc61b85820f1761a33575Timo Sirainen ret = mail_index_lock(index, F_RDLCK, MAIL_INDEX_LOCK_SECS,
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen mail_index_set_error(index, "Timeout while waiting for release of "
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen "shared lock for index file %s",
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainenstatic int mail_index_copy(struct mail_index *index)
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen fd = mail_index_create_tmp_file(index, &path);
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* write base header */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* write extended headers */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen hdr_ext = CONST_PTR_OFFSET(hdr, hdr->base_header_size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = write_full(fd, hdr_ext, hdr->header_size -
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen if (ret < 0 || write_full(fd, index->map->records,
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen mail_index_file_set_syscall_error(index, path, "write_full()");
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainenstatic int mail_index_lock_exclusive_copy(struct mail_index *index)
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* copy the index to index.tmp and use it */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenint mail_index_lock_exclusive(struct mail_index *index,
f6d5c9fbdac9af5c4d3f467f828dc6f056309d5eTimo Sirainen unsigned int *lock_id_r)
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* exclusive transaction log lock protects exclusive locking
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen for the main index file */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* if header size is smaller than what we have, we'll have to recreate
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen the index to grow it. so don't even try regular locking. */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen if (index->map->hdr.base_header_size >= sizeof(*index->hdr) ||
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* wait two seconds for exclusive lock */
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen ret = mail_index_lock(index, F_WRLCK, 2, TRUE, lock_id_r);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (mail_index_lock_exclusive_copy(index) < 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic int mail_index_copy_lock_finish(struct mail_index *index)
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen mail_index_file_set_syscall_error(index, index->copy_lock_path,
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (rename(index->copy_lock_path, index->filepath) < 0) {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen mail_index_set_error(index, "rename(%s, %s) failed: %m",
58816241cbaf79e3f8dd7d831b7c6f02c6c38ee6Timo Sirainenstatic void mail_index_excl_unlock_finish(struct mail_index *index)
58816241cbaf79e3f8dd7d831b7c6f02c6c38ee6Timo Sirainen if (index->map != NULL && index->map->write_to_disk) {
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen /* new mapping replaces the old */
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen index->lock_method != MAIL_INDEX_LOCK_DOTLOCK) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen /* leave ourself shared locked. */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen (void)mail_index_lock_fd(index, index->filepath, index->fd,
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainenvoid mail_index_unlock(struct mail_index *index, unsigned int lock_id)
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen /* shared lock */
1b97a59edb073e9a89ac43a21a9abe5d590d4a56Timo Sirainen /* unlocking some older generation of the index file.
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen we've already closed the file so just ignore this. */
0beb5d6c661ee68130a954ed0f31a34c19195fb7Timo Sirainen /* exclusive lock */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen if (index->shared_lock_count == 0 && index->excl_lock_count == 0) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen if (index->lock_method != MAIL_INDEX_LOCK_DOTLOCK) {
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen (void)mail_index_lock_fd(index, index->filepath,
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainenint mail_index_is_locked(struct mail_index *index, unsigned int lock_id)