file-lock.c revision 8c8f7ac580b661aee3d8b8dd37df4a9b41c77000
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2002-2012 Dovecot authors, see the included COPYING file */
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen#include "lib.h"
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen#include "file-lock.h"
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen#ifdef HAVE_FLOCK
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen# include <sys/file.h>
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen#endif
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainenstruct file_lock {
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen int fd;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen char *path;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int lock_type;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen enum file_lock_method lock_method;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen};
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainenbool file_lock_method_parse(const char *name, enum file_lock_method *method_r)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (strcasecmp(name, "fcntl") == 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen *method_r = FILE_LOCK_METHOD_FCNTL;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen else if (strcasecmp(name, "flock") == 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen *method_r = FILE_LOCK_METHOD_FLOCK;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen else if (strcasecmp(name, "dotlock") == 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen *method_r = FILE_LOCK_METHOD_DOTLOCK;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen else
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return FALSE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return TRUE;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenconst char *file_lock_method_to_str(enum file_lock_method method)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen switch (method) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case FILE_LOCK_METHOD_FCNTL:
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return "fcntl";
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case FILE_LOCK_METHOD_FLOCK:
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return "flock";
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case FILE_LOCK_METHOD_DOTLOCK:
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return "dotlock";
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_unreached();
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenint file_try_lock(int fd, const char *path, int lock_type,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen enum file_lock_method lock_method,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen struct file_lock **lock_r)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return file_wait_lock(fd, path, lock_type, lock_method, 0, lock_r);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen}
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainenstatic int file_lock_do(int fd, const char *path, int lock_type,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen enum file_lock_method lock_method,
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen unsigned int timeout_secs)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen{
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen int ret;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_assert(fd != -1);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (timeout_secs != 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen alarm(timeout_secs);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen switch (lock_method) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen case FILE_LOCK_METHOD_FCNTL: {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#ifndef HAVE_FCNTL
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen i_fatal("fcntl() locks not supported");
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen#else
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen struct flock fl;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen const char *errstr;
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen fl.l_type = lock_type;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen fl.l_whence = SEEK_SET;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen fl.l_start = 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen fl.l_len = 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen ret = fcntl(fd, timeout_secs ? F_SETLKW : F_SETLK, &fl);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (timeout_secs != 0) alarm(0);
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen
c59b9c273b41f7bcf51f6803110b67813879ff05Timo Sirainen if (ret == 0)
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen break;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen if (timeout_secs == 0 &&
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen (errno == EACCES || errno == EAGAIN)) {
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen /* locked by another process */
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen return 0;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen }
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen if (errno == EINTR) {
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen /* most likely alarm hit, meaning we timeouted.
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen even if not, we probably want to be killed
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen so stop blocking. */
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen errno = EAGAIN;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen return 0;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen }
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen errstr = errno != EACCES ? strerror(errno) :
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen "File is locked by another process (EACCES)";
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen i_error("fcntl(%s) locking failed for file %s: %s",
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen lock_type == F_UNLCK ? "unlock" :
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen lock_type == F_RDLCK ? "read-lock" : "write-lock",
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen path, errstr);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen return -1;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen#endif
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen }
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen case FILE_LOCK_METHOD_FLOCK: {
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen#ifndef HAVE_FLOCK
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen i_fatal("flock() locks not supported");
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen#else
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen int operation = timeout_secs != 0 ? 0 : LOCK_NB;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen switch (lock_type) {
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen case F_RDLCK:
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen operation |= LOCK_SH;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen break;
2c50ccaa9adb7df8cb49a240909fce732da57bedTimo Sirainen case F_WRLCK:
e3e5ca6aec3efe6ef7419f411d934a5350f06df9Timo Sirainen operation |= LOCK_EX;
e3e5ca6aec3efe6ef7419f411d934a5350f06df9Timo Sirainen break;
e3e5ca6aec3efe6ef7419f411d934a5350f06df9Timo Sirainen case F_UNLCK:
e3e5ca6aec3efe6ef7419f411d934a5350f06df9Timo Sirainen operation |= LOCK_UN;
e3e5ca6aec3efe6ef7419f411d934a5350f06df9Timo Sirainen break;
e3e5ca6aec3efe6ef7419f411d934a5350f06df9Timo Sirainen }
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen ret = flock(fd, operation);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen if (timeout_secs != 0) alarm(0);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen if (ret == 0)
2c50ccaa9adb7df8cb49a240909fce732da57bedTimo Sirainen break;
2c50ccaa9adb7df8cb49a240909fce732da57bedTimo Sirainen
2c50ccaa9adb7df8cb49a240909fce732da57bedTimo Sirainen if (errno == EWOULDBLOCK || errno == EINTR) {
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen /* a) locked by another process,
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen b) timeouted */
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return 0;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen i_error("flock(%s) locking failed for file %s: %m",
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen lock_type == F_UNLCK ? "unlock" :
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen lock_type == F_RDLCK ? "read-lock" : "write-lock",
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen path);
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen return -1;
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen#endif
b9f564d00b7a115f465ffd6840341c7b8f9bfc8aTimo Sirainen }
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen case FILE_LOCK_METHOD_DOTLOCK:
1554bed8d2b4e4286c10f7d6bcf716b246bd5bafTimo Sirainen /* we shouldn't get here */
1554bed8d2b4e4286c10f7d6bcf716b246bd5bafTimo Sirainen i_unreached();
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen }
7631f16156aca373004953fe6b01a7f343fb47e0Timo Sirainen
7631f16156aca373004953fe6b01a7f343fb47e0Timo Sirainen return 1;
2c50ccaa9adb7df8cb49a240909fce732da57bedTimo Sirainen}
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainenint file_wait_lock(int fd, const char *path, int lock_type,
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen enum file_lock_method lock_method,
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen unsigned int timeout_secs,
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen struct file_lock **lock_r)
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen{
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen struct file_lock *lock;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen int ret;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen ret = file_lock_do(fd, path, lock_type, lock_method, timeout_secs);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen if (ret <= 0)
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen return ret;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
90e39174d397567c101dbf694761371af3682928Timo Sirainen lock = i_new(struct file_lock, 1);
99695d99930b35c2bac85d52e976b44cf8485d83Timo Sirainen lock->fd = fd;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen lock->path = i_strdup(path);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen lock->lock_type = lock_type;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen lock->lock_method = lock_method;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen *lock_r = lock;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen return 1;
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen}
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainenint file_lock_try_update(struct file_lock *lock, int lock_type)
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen{
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen return file_lock_do(lock->fd, lock->path, lock_type,
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen lock->lock_method, 0);
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen}
e8762c2b4914db7997fa9eb644a91586952d1876Timo Sirainen
void file_unlock(struct file_lock **_lock)
{
struct file_lock *lock = *_lock;
*_lock = NULL;
if (file_lock_do(lock->fd, lock->path, F_UNLCK,
lock->lock_method, 0) == 0) {
/* this shouldn't happen */
i_error("file_unlock(%s) failed: %m", lock->path);
}
file_lock_free(&lock);
}
void file_lock_free(struct file_lock **_lock)
{
struct file_lock *lock = *_lock;
*_lock = NULL;
i_free(lock->path);
i_free(lock);
}