mail-lockdir.c revision b045ca1be3be3c034584665f64ec3823c28c1229
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen/* Copyright (C) 2002 Timo Sirainen */
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#include "lib.h"
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#include "hostpid.h"
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#include "unlink-lockfiles.h"
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#include "mail-index.h"
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#include "mail-index-util.h"
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#include "mail-lockdir.h"
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#include <time.h>
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#include <stdlib.h>
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#include <unistd.h>
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#include <fcntl.h>
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#include <sys/stat.h>
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#define DIRLOCK_FILE_PREFIX ".imap.dirlock"
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen/* 0.1 .. 0.2msec */
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen/* The dirlock should be used only while creating the index file. After the
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen header is written, the file itself should be locked and dirlock dropped
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen before index is built. So, this value shouldn't be very large, probably
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen even a few seconds would more than enough but we'll use a safe 10 seconds
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen by default. */
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen#define MAX_LOCK_WAIT_SECONDS 10
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen/* Non-local locks have a life time of 30 minutes, just to be sure that
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen small clock differences won't break things. */
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen#define NFS_LOCK_TIMEOUT (60*30)
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainenstatic int mail_index_cleanup_dir_locks(const char *dir)
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen{
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen const char *hostprefix, *path;
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen struct stat st;
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen hostprefix = t_strconcat(DIRLOCK_FILE_PREFIX ".",
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen my_hostname, ".", NULL);
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen unlink_lockfiles(dir, hostprefix, DIRLOCK_FILE_PREFIX ".",
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen time(NULL) - NFS_LOCK_TIMEOUT);
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen /* if hard link count has dropped to 1, we've unlocked the file */
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen path = t_strconcat(dir, "/" DIRLOCK_FILE_PREFIX, NULL);
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen if (stat(path, &st) == 0 && st.st_nlink == 1) {
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen /* only itself, safe to delete */
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen (void)unlink(path);
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen return TRUE;
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen }
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen return FALSE;
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen}
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainenstatic int mail_index_unlock_dir(MailIndex *index, const char *private_path,
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen const char *lockpath)
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen{
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen struct stat st, lockst;
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen if (stat(lockpath, &st) < 0)
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen return index_file_set_syscall_error(index, lockpath, "stat()");
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen if (st.st_nlink > 1) {
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen /* make sure we're really the one who's locked it */
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen if (stat(private_path, &lockst) < 0) {
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen return index_file_set_syscall_error(index, private_path,
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen "stat()");
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen }
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen if (st.st_dev != lockst.st_dev ||
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen st.st_ino != lockst.st_ino) {
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen index_set_error(index, "Unlocking file %s failed: "
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen "we're not the lock owner "
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen "(%lu,%lu vs %lu,%lu)", lockpath,
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen (unsigned long) st.st_dev,
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen (unsigned long) st.st_ino,
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen (unsigned long) lockst.st_dev,
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen (unsigned long) lockst.st_ino);
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen return FALSE;
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen }
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen }
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen /* first unlink the actual lock file */
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen if (unlink(lockpath) < 0) {
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen index_file_set_syscall_error(index, lockpath, "unlink()");
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen return FALSE;
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen }
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen if (unlink(private_path) < 0) {
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen /* non-fatal */
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen index_file_set_syscall_error(index, private_path, "unlink()");
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen }
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen return TRUE;
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen}
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainenint mail_index_lock_dir(MailIndex *index, MailLockType lock_type)
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen{
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen struct stat st;
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen const char *private_path, *lockpath;
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen int fd, orig_errno, first;
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen time_t max_wait_time;
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen i_assert(lock_type == MAIL_LOCK_EXCLUSIVE ||
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen lock_type == MAIL_LOCK_UNLOCK);
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen hostpid_init();
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen /* use .dirlock.host.pid as our lock indicator file and
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen .dirlock as the real lock */
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen private_path = t_strconcat(index->dir, "/" DIRLOCK_FILE_PREFIX ".",
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen my_hostname, ".", my_pid, NULL);
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen lockpath = t_strconcat(index->dir, "/" DIRLOCK_FILE_PREFIX, NULL);
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen if (lock_type == MAIL_LOCK_UNLOCK)
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen return mail_index_unlock_dir(index, private_path, lockpath);
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen (void)unlink(private_path);
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen fd = open(private_path, O_RDWR | O_CREAT | O_EXCL, 0660);
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen if (fd == -1) {
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen if (errno == ENOSPC)
9e86ad9eb313004cd4c8b5427daeb4c241b57af6Timo Sirainen index->nodiskspace = TRUE;
02b32cf39a098edf60981fc228e4b034f11f3b90Timo Sirainen index_file_set_syscall_error(index, private_path, "open()");
return FALSE;
}
/* try to link the file into lock file. */
first = TRUE; max_wait_time = time(NULL) + MAX_LOCK_WAIT_SECONDS;
while (link(private_path, lockpath) < 0) {
if (errno == ENOSPC)
index->nodiskspace = TRUE;
if (errno != EEXIST) {
orig_errno = errno;
/* NFS may die and link() fail even if it really
was created */
if (stat(private_path, &st) == 0 && st.st_nlink == 2)
break;
errno = orig_errno;
index_set_error(index, "link(%s, %s) lock failed: %m",
private_path, lockpath);
return FALSE;
}
if (first) {
/* cleanup lock files once */
first = FALSE;
if (mail_index_cleanup_dir_locks(index->dir))
continue; /* lock was deleted, try again */
}
if (time(NULL) > max_wait_time) {
index_set_error(index,
"Timeout waiting for lock in directory %s",
index->dir);
return FALSE;
}
usleep(LOCK_RANDOM_USLEEP_TIME);
}
return TRUE;
}