mail-index-lock.c revision 5c1a8aee989af87bddefd71e2aa83aa2bd695155
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen Locking is meant to be as transparent as possible. Anything that locks
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen the index must either keep it only a short time, or be prepared that the
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen lock is lost.
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen Lock is lost in only one situation: when we try to get an exclusive lock
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen but we already have a shared lock. Then we'll drop all shared locks and
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen get the exclusive lock.
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen Locking should never fail or timeout. Exclusive locks must be kept as short
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen time as possible. Shared locks can be long living, so if can't get exclusive
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen lock directly within 2 seconds, we'll replace the index file with a copy of
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen it. That means the shared lock holders can keep using the old file while
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen we're modifying the new file.
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen lock_id is used to figure out if acquired lock is still valid. Shared
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen locks have even numbers, exclusive locks have odd numbers. The number is
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen increased by two every time the lock is dropped.
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_lock_shared() -> lock_id=2
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_lock_shared() -> lock_id=2
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_lock_exclusive() -> lock_id=5 (had to drop shared locks)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_lock_shared() -> lock_id=4
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen Only 4 and 5 locks are valid at this time.
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic int mail_index_reopen(struct mail_index *index, int fd)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_syscall_error(index, "close()");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen ret = fd < 0 ? mail_index_try_open(index, NULL) :
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen // FIXME: serious problem, we'll just crash later..
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic int mail_index_has_changed(struct mail_index *index)
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen return mail_index_set_syscall_error(index, "fstat()");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen return mail_index_set_syscall_error(index, "stat()");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic int mail_index_lock_mprotect(struct mail_index *index, int lock_type)
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen lock_type == F_WRLCK ? (PROT_READ|PROT_WRITE) :
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_syscall_error(index, "mprotect()");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic int mail_index_lock(struct mail_index *index, int lock_type,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen unsigned int *lock_id_r)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* FIXME: exclusive locking will rewrite the index file every
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen time. shouldn't really be needed.. reading doesn't require
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen locks then, though */
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen if (update_index && index->lock_type == F_UNLCK) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (mail_index_lock_mprotect(index, lock_type) < 0)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (lock_type == F_WRLCK && index->lock_type == F_RDLCK) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* drop shared locks */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_syscall_error(index, "file_wait_lock()");
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen index->lock_id += 2; /* make sure failures below work right */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (index->excl_lock_count > 0 || index->shared_lock_count > 0) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen i_assert(lock_type == F_RDLCK || index->excl_lock_count > 0);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen ret = file_wait_lock_full(index->fd, lock_type, timeout_secs,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_syscall_error(index, "file_wait_lock()");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* we need to have the latest index file locked -
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen check if it's been updated. */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if ((ret = mail_index_has_changed(index)) < 0) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (mail_index_lock_mprotect(index, lock_type) < 0)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenint mail_index_lock_shared(struct mail_index *index, int update_index,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen unsigned int *lock_id_r)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen ret = mail_index_lock(index, F_RDLCK, DEFAULT_LOCK_TIMEOUT,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_error(index, "Timeout while waiting for release of "
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "shared fcntl() lock for index file %s",
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic int mail_index_copy(struct mail_index *index)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen fd = mail_index_create_tmp_file(index, &path);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen (void)mail_index_lock_mprotect(index, F_RDLCK);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen ret = write_full(fd, index->map->hdr, sizeof(*index->map->hdr));
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (ret < 0 || write_full(fd, index->map->records,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen sizeof(struct mail_index_record)) < 0) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen mail_index_file_set_syscall_error(index, path, "write_full()");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic int mail_index_lock_exclusive_copy(struct mail_index *index)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* copy the index to index.tmp and use it. when */
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen /* FIXME: do this without another reopen which drops locks
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen and causes potential crashes */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* go back to old index */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenint mail_index_lock_exclusive(struct mail_index *index,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen unsigned int *lock_id_r)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* exclusive transaction log lock protects exclusive locking
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen for the main index file */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* wait two seconds for exclusive lock */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen ret = mail_index_lock(index, F_WRLCK, 2, TRUE, lock_id_r);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic int mail_index_copy_lock_finish(struct mail_index *index)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* leave ourself shared locked. */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "file_try_lock()");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_file_set_syscall_error(index, index->copy_lock_path,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (rename(index->copy_lock_path, index->filepath) < 0) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_error(index, "rename(%s, %s) failed: %m",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic void mail_index_excl_unlock_finish(struct mail_index *index)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (index->map != NULL && index->map->write_to_disk) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* new mapping replaces the old */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenvoid mail_index_unlock(struct mail_index *index, unsigned int lock_id)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* shared lock */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* exclusive lock */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (index->shared_lock_count == 0 && index->excl_lock_count == 0) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen (void)mail_index_lock_mprotect(index, F_UNLCK);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "file_wait_lock()");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenint mail_index_is_locked(struct mail_index *index, unsigned int lock_id)