Mercurial > dovecot > original-hg > dovecot-1.2
diff src/lib-index/mail-lockdir.c @ 0:3b1985cbc908 HEAD
Initial revision
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 09 Aug 2002 12:15:38 +0300 |
parents | |
children | ca6967899c05 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-index/mail-lockdir.c Fri Aug 09 12:15:38 2002 +0300 @@ -0,0 +1,158 @@ +/* Copyright (C) 2002 Timo Sirainen */ + +#include "lib.h" +#include "hostpid.h" +#include "unlink-lockfiles.h" +#include "mail-index.h" +#include "mail-index-util.h" +#include "mail-lockdir.h" + +#include <time.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> + +#define DIRLOCK_FILE_PREFIX ".imap.dirlock" + +/* 0.1 .. 0.2msec */ +#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000) + +/* The dirlock should be used only while creating the index file. After the + header is written, the file itself should be locked and dirlock dropped + before index is built. So, this value shouldn't be very large, probably + even a few seconds would more than enough but we'll use a safe 10 seconds + by default. */ +#define MAX_LOCK_WAIT_SECONDS 10 + +/* Non-local locks have a life time of 30 minutes, just to be sure that + small clock differences won't break things. */ +#define NFS_LOCK_TIMEOUT (60*30) + +static int mail_index_cleanup_dir_locks(const char *dir) +{ + const char *hostprefix, *path; + struct stat st; + + hostprefix = t_strconcat(DIRLOCK_FILE_PREFIX ".", + my_hostname, ".", NULL); + + unlink_lockfiles(dir, hostprefix, DIRLOCK_FILE_PREFIX ".", + time(NULL) - NFS_LOCK_TIMEOUT); + + /* if hard link count has dropped to 1, we've unlocked the file */ + path = t_strconcat(dir, "/" DIRLOCK_FILE_PREFIX, NULL); + if (stat(path, &st) == 0 && st.st_nlink == 1) { + /* only itself, safe to delete */ + (void)unlink(path); + return TRUE; + } + + return FALSE; +} + +static int mail_index_unlock_dir(MailIndex *index, const char *path, + const char *lockpath) +{ + struct stat st, lockst; + + if (stat(lockpath, &st) != 0) { + index_set_error(index, "stat() failed for lock file %s: %m", + lockpath); + return FALSE; + } + + if (st.st_nlink > 1) { + /* make sure we're really the one who's locked it */ + if (stat(path, &lockst) != 0) { + index_set_error(index, "stat() failed for lock file " + "%s: %m", path); + return FALSE; + } + + if (st.st_dev != lockst.st_dev || + st.st_ino != lockst.st_ino) { + index_set_error(index, "Unlocking file %s failed: " + "we're not the lock owner " + "(%lu,%lu vs %lu,%lu)", lockpath, + (unsigned long) st.st_dev, + (unsigned long) st.st_ino, + (unsigned long) lockst.st_dev, + (unsigned long) lockst.st_ino); + return FALSE; + } + } + + /* first unlink the actual lock file */ + if (unlink(lockpath) == -1) { + index_set_error(index, "unlink() failed for lock file %s: %m", + lockpath); + return FALSE; + } + + (void)unlink(path); + return TRUE; +} + +int mail_index_lock_dir(MailIndex *index, MailLockType lock_type) +{ + struct stat st; + const char *path, *lockpath; + int fd, orig_errno, first; + time_t max_wait_time; + + i_assert(lock_type == MAIL_LOCK_EXCLUSIVE || + lock_type == MAIL_LOCK_UNLOCK); + + hostpid_init(); + + /* use .dirlock.host.pid as our lock indicator file and + .dirlock as the real lock */ + path = t_strconcat(index->dir, "/" DIRLOCK_FILE_PREFIX ".", + my_hostname, ".", my_pid, NULL); + lockpath = t_strconcat(index->dir, "/" DIRLOCK_FILE_PREFIX, NULL); + + if (lock_type == MAIL_LOCK_UNLOCK) + return mail_index_unlock_dir(index, path, lockpath); + + (void)unlink(path); + fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0660); + if (fd == -1) { + index_set_error(index, "Can't create lock file %s: %m", path); + return FALSE; + } + + /* try to link the file into lock file. */ + first = TRUE; max_wait_time = time(NULL) + MAX_LOCK_WAIT_SECONDS; + while (link(path, lockpath) == -1) { + if (errno != EEXIST) { + orig_errno = errno; + + /* NFS may die and link() fail even if it really + was created */ + if (stat(path, &st) == 0 && st.st_nlink == 2) + break; + + index_set_error(index, "link(%s, %s) lock failed: %m", + 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 lock in " + "directory %s", index->dir); + return FALSE; + } + + usleep(LOCK_RANDOM_USLEEP_TIME); + } + + return TRUE; +}