mail-index-lock.c revision 6c80172147a5a1cf937dea3f0f02b6fabcea861c
1516N/A/* Copyright (c) 2003-2010 Dovecot authors, see the included COPYING file */
281N/A
281N/A/*
281N/A Locking should never fail or timeout. Exclusive locks must be kept as short
281N/A time as possible. Shared locks can be long living, so if we can't get
281N/A exclusive lock directly, we'll recreate the index. That means the shared
281N/A lock holders can keep using the old file.
281N/A
281N/A lock_id is used to figure out if acquired lock is still valid. When index
281N/A file is reopened, the lock_id can become invalid. It doesn't matter however,
281N/A as no-one's going to modify the old file anymore.
281N/A
281N/A lock_id also tells us if we're referring to a shared or an exclusive lock.
281N/A This allows us to drop back to shared locking once all exclusive locks
281N/A are dropped. Shared locks have even numbers, exclusive locks have odd numbers.
281N/A The number is increased by two every time the lock is dropped or index file
281N/A is reopened.
281N/A*/
281N/A
281N/A#include "lib.h"
281N/A#include "nfs-workarounds.h"
281N/A#include "mail-index-private.h"
281N/A
3339N/A#define MAIL_INDEX_SHARED_LOCK_TIMEOUT 120
281N/A
281N/Aint mail_index_lock_fd(struct mail_index *index, const char *path, int fd,
281N/A int lock_type, unsigned int timeout_secs,
281N/A struct file_lock **lock_r)
281N/A{
281N/A if (fd == -1) {
281N/A i_assert(MAIL_INDEX_IS_IN_MEMORY(index));
281N/A return 1;
3339N/A }
289N/A
289N/A return file_wait_lock(fd, path, lock_type, index->lock_method,
289N/A timeout_secs, lock_r);
289N/A}
289N/A
281N/Astatic int mail_index_lock(struct mail_index *index, int lock_type,
2003N/A unsigned int timeout_secs, unsigned int *lock_id_r)
3255N/A{
2003N/A int ret;
281N/A
281N/A i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK);
1846N/A
1846N/A if (lock_type == F_RDLCK && index->lock_type != F_UNLCK) {
1846N/A index->shared_lock_count++;
281N/A *lock_id_r = index->lock_id_counter;
281N/A ret = 1;
2205N/A } else if (lock_type == F_WRLCK && index->lock_type == F_WRLCK) {
2639N/A index->excl_lock_count++;
281N/A *lock_id_r = index->lock_id_counter + 1;
1739N/A ret = 1;
3449N/A } else {
2476N/A ret = 0;
2936N/A }
1739N/A
3449N/A if (ret > 0) {
1685N/A /* file is already locked */
281N/A return 1;
281N/A }
281N/A
281N/A if (index->lock_method == FILE_LOCK_METHOD_DOTLOCK &&
1685N/A !MAIL_INDEX_IS_IN_MEMORY(index)) {
281N/A /* FIXME: exclusive locking will rewrite the index file every
1739N/A time. shouldn't really be needed.. reading doesn't require
1739N/A locks then, though */
1739N/A if (lock_type == F_WRLCK)
2936N/A return 0;
1739N/A
1739N/A index->shared_lock_count++;
1685N/A index->lock_type = F_RDLCK;
281N/A *lock_id_r = index->lock_id_counter;
281N/A return 1;
281N/A }
1846N/A
1846N/A i_assert(index->file_lock == NULL);
1846N/A ret = mail_index_lock_fd(index, index->filepath, index->fd,
1846N/A lock_type, timeout_secs, &index->file_lock);
281N/A if (ret <= 0)
2936N/A return ret;
3449N/A
3449N/A if (index->lock_type == F_UNLCK)
3449N/A index->lock_id_counter += 2;
3449N/A index->lock_type = lock_type;
3449N/A
3449N/A if (lock_type == F_RDLCK) {
2936N/A index->shared_lock_count++;
3449N/A *lock_id_r = index->lock_id_counter;
3449N/A } else {
3449N/A index->excl_lock_count++;
3449N/A *lock_id_r = index->lock_id_counter + 1;
3449N/A }
3449N/A
3449N/A return 1;
2936N/A}
2936N/A
281N/Avoid mail_index_flush_read_cache(struct mail_index *index, const char *path,
281N/A int fd, bool locked)
281N/A{
281N/A if ((index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) == 0)
1855N/A return;
1855N/A
281N/A /* Assume flock() is emulated with fcntl(), because that's how most
281N/A OSes work nowadays. */
281N/A if (locked &&
2936N/A (index->lock_method == FILE_LOCK_METHOD_FCNTL ||
281N/A index->lock_method == FILE_LOCK_METHOD_FLOCK)) {
281N/A nfs_flush_read_cache_locked(path, fd);
2003N/A } else {
281N/A nfs_flush_read_cache_unlocked(path, fd);
281N/A }
281N/A}
281N/A
281N/Aint mail_index_lock_shared(struct mail_index *index, unsigned int *lock_id_r)
281N/A{
281N/A unsigned int timeout_secs;
289N/A int ret;
281N/A
281N/A timeout_secs = I_MIN(MAIL_INDEX_SHARED_LOCK_TIMEOUT,
281N/A index->max_lock_timeout_secs);
281N/A ret = mail_index_lock(index, F_RDLCK, timeout_secs, lock_id_r);
281N/A if (ret > 0) {
1552N/A mail_index_flush_read_cache(index, index->filepath,
281N/A index->fd, TRUE);
281N/A return 0;
281N/A }
1685N/A if (ret < 0)
2003N/A return -1;
281N/A
281N/A mail_index_set_error(index,
281N/A "Timeout (%us) while waiting for shared lock for index file %s",
281N/A timeout_secs, index->filepath);
289N/A index->index_lock_timeout = TRUE;
1685N/A return -1;
1685N/A}
289N/A
1685N/Aint mail_index_try_lock_exclusive(struct mail_index *index,
281N/A unsigned int *lock_id_r)
1685N/A{
281N/A int ret;
281N/A
281N/A if ((ret = mail_index_lock(index, F_WRLCK, 0, lock_id_r)) > 0) {
1685N/A mail_index_flush_read_cache(index, index->filepath,
2003N/A index->fd, TRUE);
2003N/A }
281N/A return ret;
281N/A}
1739N/A
281N/Avoid mail_index_unlock(struct mail_index *index, unsigned int *_lock_id)
1685N/A{
1685N/A unsigned int lock_id = *_lock_id;
1685N/A
1739N/A *_lock_id = 0;
281N/A
281N/A if ((lock_id & 1) == 0) {
281N/A /* shared lock */
1552N/A if (!mail_index_is_locked(index, lock_id)) {
1552N/A /* unlocking some older generation of the index file.
1552N/A we've already closed the file so just ignore this. */
1552N/A return;
1552N/A }
1552N/A
1552N/A i_assert(index->shared_lock_count > 0);
1552N/A index->shared_lock_count--;
281N/A } else {
1739N/A /* exclusive lock */
1739N/A i_assert(lock_id == index->lock_id_counter + 1);
1739N/A i_assert(index->excl_lock_count > 0);
1739N/A i_assert(index->lock_type == F_WRLCK);
1739N/A if (--index->excl_lock_count == 0 &&
2003N/A index->shared_lock_count > 0) {
281N/A /* drop back to a shared lock. */
281N/A index->lock_type = F_RDLCK;
281N/A (void)file_lock_try_update(index->file_lock, F_RDLCK);
281N/A }
281N/A }
1685N/A
1685N/A if (index->shared_lock_count == 0 && index->excl_lock_count == 0) {
281N/A index->lock_id_counter += 2;
1685N/A index->lock_type = F_UNLCK;
1685N/A if (index->lock_method != FILE_LOCK_METHOD_DOTLOCK) {
281N/A if (!MAIL_INDEX_IS_IN_MEMORY(index))
281N/A file_unlock(&index->file_lock);
281N/A }
281N/A i_assert(index->file_lock == NULL);
3171N/A }
2003N/A}
2003N/A
2003N/Abool mail_index_is_locked(struct mail_index *index, unsigned int lock_id)
2003N/A{
2003N/A if ((index->lock_id_counter ^ lock_id) <= 1 && lock_id != 0) {
2003N/A i_assert(index->lock_type != F_UNLCK);
2003N/A return TRUE;
2003N/A }
2003N/A
2003N/A return FALSE;
2003N/A}
3030N/A