mail-index-lock.c revision fe4d93fb06c39c82802c5a5ab9ddb3251863cf6c
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster/* Copyright (C) 2003-2004 Timo Sirainen */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster/*
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster Locking should never fail or timeout. Exclusive locks must be kept as short
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster time as possible. Shared locks can be long living, so if we can't get
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster exclusive lock directly within 2 seconds, we'll replace the index file with
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster a copy of it. That means the shared lock holders can keep using the old file
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster while we're modifying the new file.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster lock_id is used to figure out if acquired lock is still valid. When index
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster file is reopened, the lock_id can become invalid. It doesn't matter however,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster as no-one's going to modify the old file anymore.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster lock_id also tells if we're referring to shared or exclusive lock. This
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster allows us to drop back to shared locking once all exclusive locks are
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster dropped. Shared locks have even numbers, exclusive locks have odd numbers.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster The number is increased by two every time the lock is dropped or index file
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster is reopened.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster*/
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "lib.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "buffer.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "mmap-util.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "write-full.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "mail-index-private.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include <stdio.h>
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include <sys/stat.h>
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#ifdef HAVE_FLOCK
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster# include <sys/file.h>
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#endif
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#define MAIL_INDEX_LOCK_WAIT_TIME 120
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterint mail_index_lock_fd(struct mail_index *index, const char *path, int fd,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster int lock_type, unsigned int timeout_secs)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster{
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster int ret;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (fd == -1) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster i_assert(MAIL_INDEX_IS_IN_MEMORY(index));
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return 1;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (timeout_secs != 0)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster alarm(MAIL_INDEX_LOCK_WAIT_TIME);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster switch (index->lock_method) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster case MAIL_INDEX_LOCK_FCNTL: {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#ifndef HAVE_FCNTL
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster i_fatal("fcntl() locks not supported");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#else
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster struct flock fl;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster fl.l_type = lock_type;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster fl.l_whence = SEEK_SET;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster fl.l_start = 0;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster fl.l_len = 0;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster ret = fcntl(fd, timeout_secs ? F_SETLKW : F_SETLK, &fl);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (timeout_secs != 0) alarm(0);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (ret == 0)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return 1;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (timeout_secs == 0 &&
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster (errno == EACCES || errno == EAGAIN)) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* locked by another process */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return 0;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (errno == EINTR) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* most likely alarm hit, meaning we timeouted.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster even if not, we probably want to be killed
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster so stop blocking. */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster errno = EAGAIN;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return 0;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster mail_index_file_set_syscall_error(index, path, "fcntl()");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return -1;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#endif
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster case MAIL_INDEX_LOCK_FLOCK: {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#ifndef HAVE_FLOCK
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster i_fatal("flock() locks not supported "
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "(see lock_method setting in config file)");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#else
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster int operation = timeout_secs != 0 ? 0 : LOCK_NB;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster switch (lock_type) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster case F_RDLCK:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster operation |= LOCK_SH;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster case F_WRLCK:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster operation |= LOCK_EX;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster case F_UNLCK:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster operation |= LOCK_UN;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster ret = flock(fd, operation);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (timeout_secs != 0) alarm(0);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (ret == 0)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return 1;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (errno == EWOULDBLOCK || errno == EINTR) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* a) locked by another process,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster b) timeouted */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return 0;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (errno == ENOLCK) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* Give a bit more helpful error message since this
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster is the default locking method and it doesn't work
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster with NFS. */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster mail_index_set_error(index,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "flock() failed with file %s: %m "
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster "(see lock_method setting in config file)",
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster path);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return -1;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster mail_index_file_set_syscall_error(index, path, "flock()");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return -1;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#endif
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster case MAIL_INDEX_LOCK_DOTLOCK:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* we shouldn't get here */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster break;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster i_unreached();
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster}
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterstatic int mail_index_lock(struct mail_index *index, int lock_type,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster unsigned int timeout_secs, int update_index,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster unsigned int *lock_id_r)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster{
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster int ret, ret2;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (lock_type == F_RDLCK && index->lock_type != F_UNLCK) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster index->shared_lock_count++;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster *lock_id_r = index->lock_id;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster ret = 1;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster } else if (lock_type == F_WRLCK && index->lock_type == F_WRLCK) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster index->excl_lock_count++;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster *lock_id_r = index->lock_id + 1;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster ret = 1;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster } else {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster ret = 0;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (update_index && index->excl_lock_count == 0) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster i_assert(index->lock_type != F_WRLCK);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if ((ret2 = mail_index_reopen_if_needed(index)) < 0)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return -1;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (ret > 0 && ret2 == 0) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster i_assert(lock_type == F_RDLCK);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster i_assert(index->lock_type == F_RDLCK);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return 1;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster ret = 0;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (ret > 0)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return 1;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (index->lock_method == MAIL_INDEX_LOCK_DOTLOCK) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* FIXME: exclusive locking will rewrite the index file every
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster time. shouldn't really be needed.. reading doesn't require
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster locks then, though */
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (lock_type == F_WRLCK)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return 0;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (update_index && index->lock_type == F_UNLCK) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (mail_index_reopen_if_needed(index) < 0)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return -1;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster index->shared_lock_count++;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster index->lock_type = F_RDLCK;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster *lock_id_r = index->lock_id;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return 1;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster }
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (lock_type == F_RDLCK || !index->log_locked) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster ret = mail_index_lock_fd(index, index->filepath, index->fd,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster lock_type, timeout_secs);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster } else {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster /* this is kind of kludgy. we wish to avoid deadlocks while
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster trying to lock transaction log, but it can happen if our
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster process is holding transaction log lock and waiting for
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster index write lock, while the other process is holding index
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster read lock and waiting for transaction log lock.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster we don't have a problem with grabbing read index lock
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster because the only way for it to block is if it's
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster write-locked, which isn't allowed unless transaction log
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster is also locked.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster so, the workaround for this problem is that we simply try
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster locking once. if it doesn't work, just rewrite the file.
hopefully there won't be any other deadlocking issues. :) */
ret = mail_index_lock_fd(index, index->filepath, index->fd,
lock_type, 0);
}
if (ret <= 0)
return ret;
if (index->lock_type == F_UNLCK)
index->lock_id += 2;
index->lock_type = lock_type;
if (lock_type == F_RDLCK) {
index->shared_lock_count++;
*lock_id_r = index->lock_id;
} else {
index->excl_lock_count++;
*lock_id_r = index->lock_id + 1;
}
return 1;
}
int mail_index_lock_shared(struct mail_index *index, bool update_index,
unsigned int *lock_id_r)
{
int ret;
ret = mail_index_lock(index, F_RDLCK, MAIL_INDEX_LOCK_SECS,
update_index, lock_id_r);
if (ret > 0)
return 0;
if (ret < 0)
return -1;
mail_index_set_error(index,
"Timeout while waiting for shared lock for index file %s",
index->filepath);
index->index_lock_timeout = TRUE;
return -1;
}
static int mail_index_copy(struct mail_index *index)
{
struct mail_index_map *map = index->map;
unsigned int base_size;
const char *path;
int ret, fd;
i_assert(!MAIL_INDEX_IS_IN_MEMORY(index));
fd = mail_index_create_tmp_file(index, &path);
if (fd == -1)
return -1;
/* write base header */
base_size = I_MIN(map->hdr.base_header_size, sizeof(map->hdr));
ret = write_full(fd, &map->hdr, base_size);
if (ret == 0) {
/* write extended headers */
ret = write_full(fd, CONST_PTR_OFFSET(map->hdr_base, base_size),
map->hdr.header_size - base_size);
}
if (ret < 0 || write_full(fd, map->records, map->records_count *
map->hdr.record_size) < 0) {
mail_index_file_set_syscall_error(index, path, "write_full()");
(void)close(fd);
(void)unlink(path);
fd = -1;
} else {
i_assert(index->copy_lock_path == NULL);
index->copy_lock_path = i_strdup(path);
}
return fd;
}
static int mail_index_lock_exclusive_copy(struct mail_index *index)
{
int fd, old_lock_type;
i_assert(index->log_locked);
if (index->copy_lock_path != NULL) {
index->excl_lock_count++;
return 0;
}
i_assert(index->excl_lock_count == 0);
/* copy the index to index.tmp and use it */
fd = mail_index_copy(index);
if (fd == -1)
return -1;
old_lock_type = index->lock_type;
index->lock_type = F_WRLCK;
index->excl_lock_count++;
if (mail_index_reopen(index, fd) < 0) {
i_assert(index->excl_lock_count == 1);
(void)close(fd);
if (unlink(index->copy_lock_path) < 0) {
mail_index_file_set_syscall_error(index,
index->copy_lock_path,
"unlink()");
}
i_free(index->copy_lock_path);
index->copy_lock_path = NULL;
index->lock_type = old_lock_type;
index->excl_lock_count = 0;
return -1;
}
return 0;
}
int mail_index_lock_exclusive(struct mail_index *index,
unsigned int *lock_id_r)
{
int ret;
/* exclusive transaction log lock protects exclusive locking
for the main index file */
i_assert(index->log_locked);
/* if header size is smaller than what we have, we'll have to recreate
the index to grow it. so don't even try regular locking. */
if (index->map->hdr.base_header_size >= sizeof(*index->hdr) ||
index->excl_lock_count > 0) {
/* wait two seconds for exclusive lock */
ret = mail_index_lock(index, F_WRLCK, 2, TRUE, lock_id_r);
if (ret > 0)
return 0;
if (ret < 0)
return -1;
}
if (mail_index_lock_exclusive_copy(index) < 0)
return -1;
*lock_id_r = index->lock_id + 1;
return 0;
}
static int mail_index_copy_lock_finish(struct mail_index *index)
{
if (fsync(index->fd) < 0) {
mail_index_file_set_syscall_error(index, index->copy_lock_path,
"fsync()");
return -1;
}
if (rename(index->copy_lock_path, index->filepath) < 0) {
mail_index_set_error(index, "rename(%s, %s) failed: %m",
index->copy_lock_path, index->filepath);
return -1;
}
i_free(index->copy_lock_path);
index->copy_lock_path = NULL;
return 0;
}
static int mail_index_write_map_over(struct mail_index *index)
{
struct mail_index_map *map = index->map;
unsigned int base_size;
if (MAIL_INDEX_IS_IN_MEMORY(index))
return 0;
/* write records. */
if (map->write_seq_first != 0) {
size_t rec_offset =
(map->write_seq_first-1) * map->hdr.record_size;
if (pwrite_full(index->fd,
CONST_PTR_OFFSET(map->records, rec_offset),
(map->write_seq_last -
map->write_seq_first + 1) *
map->hdr.record_size,
map->hdr.header_size + rec_offset) < 0)
return -1;
}
/* write base header */
base_size = I_MIN(map->hdr.base_header_size, sizeof(map->hdr));
if (pwrite_full(index->fd, &map->hdr, base_size, 0) < 0)
return -1;
/* write extended headers */
if (pwrite_full(index->fd, CONST_PTR_OFFSET(map->hdr_base, base_size),
map->hdr.header_size - base_size, base_size) < 0)
return -1;
return 0;
}
static void mail_index_write_map(struct mail_index *index)
{
struct mail_index_map *map = index->map;
int fd;
if (map->write_atomic || index->copy_lock_path != NULL ||
index->fd == -1) {
/* write by recreating the index */
i_assert(index->log_locked);
if (index->copy_lock_path != NULL) {
/* new mapping replaces the old */
(void)unlink(index->copy_lock_path);
i_free(index->copy_lock_path);
index->copy_lock_path = NULL;
}
if (!MAIL_INDEX_IS_IN_MEMORY(index)) {
fd = mail_index_copy(index);
if (fd == -1)
mail_index_set_inconsistent(index);
else
(void)close(fd);
}
} else {
/* write the modified parts. header is small enough to be
always written, write_seq_* specifies the record range. */
if (mail_index_write_map_over(index) < 0)
mail_index_set_inconsistent(index);
}
map->write_to_disk = FALSE;
map->write_atomic = FALSE;
map->write_seq_first = map->write_seq_last = 0;
}
static void mail_index_excl_unlock_finish(struct mail_index *index)
{
if (index->map != NULL && index->map->write_to_disk)
mail_index_write_map(index);
if (index->shared_lock_count > 0 &&
index->lock_method != MAIL_INDEX_LOCK_DOTLOCK) {
/* leave ourself shared locked. */
(void)mail_index_lock_fd(index, index->filepath, index->fd,
F_RDLCK, 0);
i_assert(index->lock_type == F_WRLCK);
index->lock_type = F_RDLCK;
}
if (index->copy_lock_path != NULL) {
i_assert(index->log_locked);
if (mail_index_copy_lock_finish(index) < 0)
mail_index_set_inconsistent(index);
/* We may still have shared locks for the old file, but they
don't matter. They're invalidated when we re-open the new
index file. */
}
}
void mail_index_unlock(struct mail_index *index, unsigned int lock_id)
{
if ((lock_id & 1) == 0) {
/* shared lock */
if (!mail_index_is_locked(index, lock_id)) {
/* unlocking some older generation of the index file.
we've already closed the file so just ignore this. */
return;
}
i_assert(index->shared_lock_count > 0);
index->shared_lock_count--;
} else {
/* exclusive lock */
i_assert(lock_id == index->lock_id + 1);
i_assert(index->excl_lock_count > 0);
if (--index->excl_lock_count == 0)
mail_index_excl_unlock_finish(index);
}
if (index->shared_lock_count == 0 && index->excl_lock_count == 0) {
index->lock_id += 2;
index->lock_type = F_UNLCK;
if (index->lock_method != MAIL_INDEX_LOCK_DOTLOCK) {
(void)mail_index_lock_fd(index, index->filepath,
index->fd, F_UNLCK, 0);
}
}
}
bool mail_index_is_locked(struct mail_index *index, unsigned int lock_id)
{
if ((index->lock_id ^ lock_id) <= 1) {
i_assert(index->lock_type != F_UNLCK);
return TRUE;
}
return FALSE;
}