view src/lib-index/mbox/mbox-lock.c @ 366:0da2b09461aa HEAD

more locking fixes.
author Timo Sirainen <tss@iki.fi>
date Sun, 06 Oct 2002 09:08:26 +0300
parents cb405d2f5fd5
children 9b36fc3c1385
line wrap: on
line source

/* Copyright (C) 2002 Timo Sirainen */

#include "lib.h"
#include "mbox-index.h"
#include "mbox-lock.h"
#include "mail-index-util.h"

#include <time.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#ifdef HAVE_FLOCK
#  include <sys/file.h>
#endif

#ifdef HAVE_FLOCK
#  define USE_FLOCK
#endif

/* 0.1 .. 0.2msec */
#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)

/* abort trying to get lock after 30 seconds */
#define MAX_LOCK_WAIT_SECONDS 30

/* remove lock after 10 mins */
#define STALE_LOCK_TIMEOUT (60*10)

#ifdef USE_FLOCK
static int mbox_lock_flock(MailIndex *index, int lock_type)
{
	if (lock_type == F_WRLCK)
		lock_type = LOCK_EX;
	else if (lock_type == F_RDLCK)
		lock_type = LOCK_SH;
	else
		lock_type = LOCK_UN;

	if (flock(index->mbox_fd, lock_type) < 0)
		return index_file_set_syscall_error(index, index->mbox_path,
						    "flock()");

	return TRUE;
}

#else

static int mbox_lock_fcntl(MailIndex *index, int lock_type)
{
	struct flock fl;

	fl.l_type = lock_type;
	fl.l_whence = SEEK_SET;
	fl.l_start = 0;
	fl.l_len = 0;

	while (fcntl(index->mbox_fd, F_SETLKW, &fl) == -1) {
		if (errno != EINTR) {
			index_file_set_syscall_error(index, index->mbox_path,
						     "fcntl()");
			return FALSE;
		}
	}

	return TRUE;
}
#endif

static int mbox_lock_dotlock(MailIndex *index, const char *path, int set)
{
	struct stat st;
	time_t now, max_wait_time;
	int fd;

	path = t_strconcat(path, ".lock", NULL);
	if (!set) {
		if (unlink(path) == 0 || errno == ENOENT)
			return TRUE;

		return index_file_set_syscall_error(index, path, "unlink()");
	}

	/* don't bother with the temp files as we'd just leave them lying
	   around. besides, postfix also relies on O_EXCL working so we
	   might as well. */
	max_wait_time = time(NULL) + MAX_LOCK_WAIT_SECONDS;
	do {
		now = time(NULL);

		if (stat(path, &st) == 0) {
			/* lock exists, see if it's too old */
			if (now > st.st_ctime + STALE_LOCK_TIMEOUT && 
			    unlink(path) < 0 && errno != ENOENT) {
				index_file_set_syscall_error(index, path,
							     "unlink()");
				break;
			}

			usleep(LOCK_RANDOM_USLEEP_TIME);
			continue;
		}

		fd = open(path, O_WRONLY | O_EXCL | O_CREAT, 0);
		if (fd != -1) {
			/* got it */
			if (close(fd) < 0) {
				index_file_set_syscall_error(index, path,
							     "close()");
			}
			return TRUE;
		}

		if (errno != EEXIST) {
			index_file_set_syscall_error(index, path, "open()");
			return FALSE;
		}
	} while (now < max_wait_time);

	index_set_error(index, "Timeout while waiting for release of mbox "
			"dotlock %s", path);
	return FALSE;
}

static int mbox_lock(MailIndex *index, int exclusive)
{
	i_assert(index->mbox_fd != -1);

	if (++index->mbox_locks > 1)
		return TRUE;

	if (exclusive) {
		if (!mbox_lock_dotlock(index, index->mbox_path, TRUE))
			return FALSE;
	}

        index->mbox_lock_type = exclusive ? F_WRLCK : F_RDLCK;
#ifdef USE_FLOCK
	if (!mbox_lock_flock(index, index->mbox_lock_type)) {
		(void)mbox_lock_dotlock(index, index->mbox_path, FALSE);
		return FALSE;
	}
#else
	if (!mbox_lock_fcntl(index, index->mbox_lock_type)) {
		(void)mbox_lock_dotlock(index, index->mbox_path, FALSE);
		return FALSE;
	}
#endif

	return TRUE;
}

int mbox_lock_read(MailIndex *index)
{
	return mbox_lock(index, FALSE);
}

int mbox_lock_write(MailIndex *index)
{
	i_assert(index->mbox_lock_type != F_RDLCK);
	return mbox_lock(index, TRUE);
}

int mbox_unlock(MailIndex *index)
{
	int failed;

	i_assert(index->mbox_fd != -1);
	i_assert(index->mbox_locks > 0);

	if (--index->mbox_locks > 0)
		return TRUE;

	index->mbox_lock_type = F_UNLCK;
	failed = FALSE;
#ifdef USE_FLOCK
	if (!mbox_lock_flock(index, F_UNLCK))
		failed = TRUE;
#else
	if (!mbox_lock_fcntl(index, F_UNLCK))
		failed = TRUE;
#endif
	if (!mbox_lock_dotlock(index, index->mbox_path, FALSE))
		failed = TRUE;

	return !failed;
}