file-lock.c revision 1ea0aa8e14e843f2776746776a429b0a1aae299d
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "lib.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "istream.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "file-lock.h"
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen#include "time-util.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include <time.h>
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include <sys/stat.h>
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#ifdef HAVE_FLOCK
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen# include <sys/file.h>
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#endif
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstruct file_lock {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen int fd;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen char *path;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct timeval locked_time;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen int lock_type;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen enum file_lock_method lock_method;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen};
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic struct timeval lock_wait_start;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic uint64_t file_lock_wait_usecs = 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic long long file_lock_slow_warning_usecs = -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic void file_lock_log_warning_if_slow(struct file_lock *lock);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenbool file_lock_method_parse(const char *name, enum file_lock_method *method_r)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (strcasecmp(name, "fcntl") == 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *method_r = FILE_LOCK_METHOD_FCNTL;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen else if (strcasecmp(name, "flock") == 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *method_r = FILE_LOCK_METHOD_FLOCK;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen else if (strcasecmp(name, "dotlock") == 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *method_r = FILE_LOCK_METHOD_DOTLOCK;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen else
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return FALSE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenconst char *file_lock_method_to_str(enum file_lock_method method)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen switch (method) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen case FILE_LOCK_METHOD_FCNTL:
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return "fcntl";
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen case FILE_LOCK_METHOD_FLOCK:
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return "flock";
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen case FILE_LOCK_METHOD_DOTLOCK:
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return "dotlock";
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_unreached();
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenint file_try_lock(int fd, const char *path, int lock_type,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen enum file_lock_method lock_method,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct file_lock **lock_r)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return file_wait_lock(fd, path, lock_type, lock_method, 0, lock_r);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenint file_try_lock_error(int fd, const char *path, int lock_type,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen enum file_lock_method lock_method,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct file_lock **lock_r, const char **error_r)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return file_wait_lock_error(fd, path, lock_type, lock_method, 0,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen lock_r, error_r);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic const char *
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenfile_lock_find_fcntl(int lock_fd, int lock_type)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct flock fl;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen memset(&fl, 0, sizeof(fl));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen fl.l_type = lock_type;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen fl.l_whence = SEEK_SET;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen fl.l_start = 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen fl.l_len = 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (fcntl(lock_fd, F_GETLK, &fl) < 0 ||
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen fl.l_type == F_UNLCK || fl.l_pid == -1 || fl.l_pid == 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return "";
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return t_strdup_printf(" (%s lock held by pid %ld)",
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen fl.l_type == F_RDLCK ? "READ" : "WRITE", (long)fl.l_pid);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic const char *
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenfile_lock_find_proc_locks(int lock_fd ATTR_UNUSED)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* do anything except Linux support this? don't bother trying it for
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen OSes we don't know about. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#ifdef __linux__
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen static bool have_proc_locks = TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct stat st;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen char node_buf[MAX_INT_STRLEN*3 + 2 + 1];
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct istream *input;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *line, *lock_type = "";
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen pid_t pid = 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen int fd;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (!have_proc_locks)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return NULL;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (fstat(lock_fd, &st) < 0)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return "";
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_snprintf(node_buf, sizeof(node_buf), "%02x:%02x:%llu",
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen major(st.st_dev), minor(st.st_dev),
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen (unsigned long long)st.st_ino);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen fd = open("/proc/locks", O_RDONLY);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (fd == -1) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen have_proc_locks = FALSE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return "";
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen input = i_stream_create_fd_autoclose(&fd, 512);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen while (pid == 0 && (line = i_stream_read_next_line(input)) != NULL) T_BEGIN {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *const *args = t_strsplit_spaces(line, " ");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* number: FLOCK/POSIX ADVISORY READ/WRITE pid
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen major:minor:inode region-start region-end */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (str_array_length(args) < 8) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ; /* don't continue from within a T_BEGIN {...} T_END */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen } else if (strcmp(args[5], node_buf) == 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen lock_type = strcmp(args[3], "READ") == 0 ?
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "READ" : "WRITE";
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (str_to_pid(args[4], &pid) < 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen pid = 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen } T_END;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_stream_destroy(&input);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (pid == 0) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* not found */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return "";
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (pid == getpid())
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return " (BUG: lock is held by our own process)";
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return t_strdup_printf(" (%s lock held by pid %ld)", lock_type, (long)pid);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#else
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return "";
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#endif
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenconst char *file_lock_find(int lock_fd, enum file_lock_method lock_method,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen int lock_type)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *ret;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (lock_method == FILE_LOCK_METHOD_FCNTL) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ret = file_lock_find_fcntl(lock_fd, lock_type);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ret[0] != '\0')
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return ret;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return file_lock_find_proc_locks(lock_fd);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic bool err_is_lock_timeout(time_t started, unsigned int timeout_secs)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* if EINTR took at least timeout_secs-1 number of seconds,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen assume it was the alarm. otherwise log EINTR failure.
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (We most likely don't want to retry EINTR since a signal
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen means somebody wants us to stop blocking). */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return errno == EINTR &&
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (unsigned long)(time(NULL) - started + 1) >= timeout_secs;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic int file_lock_do(int fd, const char *path, int lock_type,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen enum file_lock_method lock_method,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen unsigned int timeout_secs, const char **error_r)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen const char *lock_type_str;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen time_t started = time(NULL);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen int ret;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_assert(fd != -1);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (timeout_secs != 0) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen alarm(timeout_secs);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen file_lock_wait_start();
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen lock_type_str = lock_type == F_UNLCK ? "unlock" :
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (lock_type == F_RDLCK ? "read-lock" : "write-lock");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen switch (lock_method) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen case FILE_LOCK_METHOD_FCNTL: {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#ifndef HAVE_FCNTL
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *error_r = t_strdup_printf(
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "Can't lock file %s: fcntl() locks not supported", path);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#else
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct flock fl;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen fl.l_type = lock_type;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen fl.l_whence = SEEK_SET;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen fl.l_start = 0;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen fl.l_len = 0;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen ret = fcntl(fd, timeout_secs != 0 ? F_SETLKW : F_SETLK, &fl);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (timeout_secs != 0) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen alarm(0);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen file_lock_wait_end(path);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (ret == 0)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen break;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (timeout_secs == 0 &&
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen (errno == EACCES || errno == EAGAIN)) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* locked by another process */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen *error_r = t_strdup_printf(
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "fcntl(%s, %s, F_SETLK) locking failed: %m "
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "(File is already locked)", path, lock_type_str);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (err_is_lock_timeout(started, timeout_secs)) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen errno = EAGAIN;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen *error_r = t_strdup_printf(
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen "fcntl(%s, %s, F_SETLKW) locking failed: "
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "Timed out after %u seconds%s",
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen path, lock_type_str, timeout_secs,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen file_lock_find(fd, lock_method, lock_type));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *error_r = t_strdup_printf("fcntl(%s, %s, %s) locking failed: %m",
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen path, lock_type_str, timeout_secs == 0 ? "F_SETLK" : "F_SETLKW");
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (errno == EDEADLK)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_panic("%s%s", *error_r, file_lock_find(fd, lock_method, lock_type));
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return -1;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen#endif
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen case FILE_LOCK_METHOD_FLOCK: {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen#ifndef HAVE_FLOCK
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen *error_r = t_strdup_printf(
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "Can't lock file %s: flock() not supported", path);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return -1;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen#else
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen int operation = timeout_secs != 0 ? 0 : LOCK_NB;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen switch (lock_type) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen case F_RDLCK:
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen operation |= LOCK_SH;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen break;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen case F_WRLCK:
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen operation |= LOCK_EX;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen break;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen case F_UNLCK:
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen operation |= LOCK_UN;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen break;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen }
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen ret = flock(fd, operation);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen if (timeout_secs != 0) {
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen alarm(0);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen file_lock_wait_end(path);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen }
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen if (ret == 0)
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen break;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen if (timeout_secs == 0 && errno == EWOULDBLOCK) {
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen /* locked by another process */
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen *error_r = t_strdup_printf(
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen "flock(%s, %s) failed: %m "
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen "(File is already locked)", path, lock_type_str);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (err_is_lock_timeout(started, timeout_secs)) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen errno = EAGAIN;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen *error_r = t_strdup_printf("flock(%s, %s) failed: "
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "Timed out after %u seconds%s",
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen path, lock_type_str, timeout_secs,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen file_lock_find(fd, lock_method, lock_type));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *error_r = t_strdup_printf("flock(%s, %s) failed: %m",
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen path, lock_type_str);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (errno == EDEADLK)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_panic("%s%s", *error_r, file_lock_find(fd, lock_method, lock_type));
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return -1;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen#endif
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen case FILE_LOCK_METHOD_DOTLOCK:
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* we shouldn't get here */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_unreached();
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return 1;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen}
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenint file_wait_lock(int fd, const char *path, int lock_type,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen enum file_lock_method lock_method,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen unsigned int timeout_secs,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct file_lock **lock_r)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen const char *error;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen int ret;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen ret = file_wait_lock_error(fd, path, lock_type, lock_method,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen timeout_secs, lock_r, &error);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (ret < 0)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_error("%s", error);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return ret;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen}
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenint file_wait_lock_error(int fd, const char *path, int lock_type,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen enum file_lock_method lock_method,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen unsigned int timeout_secs,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct file_lock **lock_r, const char **error_r)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct file_lock *lock;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen int ret;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ret = file_lock_do(fd, path, lock_type, lock_method, timeout_secs, error_r);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ret <= 0)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return ret;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen lock = i_new(struct file_lock, 1);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen lock->fd = fd;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen lock->path = i_strdup(path);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen lock->lock_type = lock_type;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen lock->lock_method = lock_method;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (gettimeofday(&lock->locked_time, NULL) < 0)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_fatal("gettimeofday() failed: %m");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *lock_r = lock;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenint file_lock_try_update(struct file_lock *lock, int lock_type)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen const char *error;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen int ret;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen ret = file_lock_do(lock->fd, lock->path, lock_type,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen lock->lock_method, 0, &error);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen if (ret <= 0)
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen return ret;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen file_lock_log_warning_if_slow(lock);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen lock->lock_type = lock_type;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return 1;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenvoid file_unlock(struct file_lock **_lock)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct file_lock *lock = *_lock;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *error;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *_lock = NULL;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (file_lock_do(lock->fd, lock->path, F_UNLCK,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen lock->lock_method, 0, &error) == 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* this shouldn't happen */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_error("file_unlock(%s) failed: %m", lock->path);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen file_lock_free(&lock);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenvoid file_lock_free(struct file_lock **_lock)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct file_lock *lock = *_lock;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *_lock = NULL;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen file_lock_log_warning_if_slow(lock);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_free(lock->path);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_free(lock);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenvoid file_lock_wait_start(void)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(lock_wait_start.tv_sec == 0);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (gettimeofday(&lock_wait_start, NULL) < 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_fatal("gettimeofday() failed: %m");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
static void file_lock_wait_init_warning(void)
{
const char *value;
i_assert(file_lock_slow_warning_usecs == -1);
value = getenv("FILE_LOCK_SLOW_WARNING_MSECS");
if (value == NULL)
file_lock_slow_warning_usecs = LLONG_MAX;
else if (str_to_llong(value, &file_lock_slow_warning_usecs) == 0 &&
file_lock_slow_warning_usecs > 0) {
file_lock_slow_warning_usecs *= 1000;
} else {
i_error("FILE_LOCK_SLOW_WARNING_MSECS: "
"Invalid value '%s' - ignoring", value);
file_lock_slow_warning_usecs = LLONG_MAX;
}
}
static void file_lock_log_warning_if_slow(struct file_lock *lock)
{
if (file_lock_slow_warning_usecs < 0)
file_lock_wait_init_warning();
if (file_lock_slow_warning_usecs == LLONG_MAX) {
/* slowness checking is disabled */
return;
}
if (lock->lock_type != F_WRLCK) {
/* some shared locks can legitimately be kept for a long time.
don't warn about them. */
return;
}
struct timeval now;
if (gettimeofday(&now, NULL) < 0)
i_fatal("gettimeofday() failed: %m");
int diff = timeval_diff_msecs(&now, &lock->locked_time);
if (diff > file_lock_slow_warning_usecs/1000) {
i_warning("Lock %s kept for %d.%03d secs", lock->path,
diff / 1000, diff % 1000);
}
}
void file_lock_wait_end(const char *lock_name)
{
struct timeval now;
i_assert(lock_wait_start.tv_sec != 0);
if (gettimeofday(&now, NULL) < 0)
i_fatal("gettimeofday() failed: %m");
long long diff = timeval_diff_usecs(&now, &lock_wait_start);
if (diff > file_lock_slow_warning_usecs) {
if (file_lock_slow_warning_usecs < 0)
file_lock_wait_init_warning();
if (diff > file_lock_slow_warning_usecs) {
int diff_msecs = (diff + 999) / 1000;
i_warning("Locking %s took %d.%03d secs", lock_name,
diff_msecs / 1000, diff_msecs % 1000);
}
}
file_lock_wait_usecs += diff;
lock_wait_start.tv_sec = 0;
}
uint64_t file_lock_wait_get_total_usecs(void)
{
return file_lock_wait_usecs;
}