file-lock.c revision d8d93ecd89efa7c84a3a21264a550449619f715b
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainenstatic long long file_lock_slow_warning_usecs = -1;
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainenstatic void file_lock_log_warning_if_slow(struct file_lock *lock);
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainenbool file_lock_method_parse(const char *name, enum file_lock_method *method_r)
50c617761ee9653bd44646a95178773a3686d62eTimo Sirainenconst char *file_lock_method_to_str(enum file_lock_method method)
661998e2ccd772ad92a9d4a75cb712692a8c94b3Timo Sirainen return "fcntl";
50c617761ee9653bd44646a95178773a3686d62eTimo Sirainen return "flock";
50c617761ee9653bd44646a95178773a3686d62eTimo Sirainen return "dotlock";
50c617761ee9653bd44646a95178773a3686d62eTimo Sirainenint file_try_lock(int fd, const char *path, int lock_type,
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainen return file_wait_lock(fd, path, lock_type, lock_method, 0, lock_r);
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainenint file_try_lock_error(int fd, const char *path, int lock_type,
01937f71b3ae0d5b30b813372f44a3e7e86c89dcTimo Sirainen struct file_lock **lock_r, const char **error_r)
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen return file_wait_lock_error(fd, path, lock_type, lock_method, 0,
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainenstatic const char *
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenfile_lock_find_fcntl(int lock_fd, int lock_type)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen fl.l_type == F_UNLCK || fl.l_pid == -1 || fl.l_pid == 0)
897368f75a68f97e172ba76540759a10c557f1c3Timo Sirainen return t_strdup_printf(" (%s lock held by pid %ld)",
1460ef7a18c53216ddb4a94bb62fba96076aae8eTimo Sirainen fl.l_type == F_RDLCK ? "READ" : "WRITE", (long)fl.l_pid);
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainenstatic const char *
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainenfile_lock_find_proc_locks(int lock_fd ATTR_UNUSED)
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen /* do anything except Linux support this? don't bother trying it for
e2d268e9531227ead6a98466ecf3c046c857ef70Timo Sirainen OSes we don't know about. */
897368f75a68f97e172ba76540759a10c557f1c3Timo Sirainen i_snprintf(node_buf, sizeof(node_buf), "%02x:%02x:%llu",
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen input = i_stream_create_fd_autoclose(&fd, 512);
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen while (pid == 0 && (line = i_stream_read_next_line(input)) != NULL) T_BEGIN {
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen const char *const *args = t_strsplit_spaces(line, " ");
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen /* number: FLOCK/POSIX ADVISORY READ/WRITE pid
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen major:minor:inode region-start region-end */
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen ; /* don't continue from within a T_BEGIN {...} T_END */
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen /* not found */
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen return " (BUG: lock is held by our own process)";
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen return t_strdup_printf(" (%s lock held by pid %ld)", lock_type, (long)pid);
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainenconst char *file_lock_find(int lock_fd, enum file_lock_method lock_method,
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainen const char *ret;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen ret = file_lock_find_fcntl(lock_fd, lock_type);
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainenstatic bool err_is_lock_timeout(time_t started, unsigned int timeout_secs)
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen /* if EINTR took at least timeout_secs-1 number of seconds,
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainen assume it was the alarm. otherwise log EINTR failure.
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen (We most likely don't want to retry EINTR since a signal
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainen means somebody wants us to stop blocking). */
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen (unsigned long)(time(NULL) - started + 1) >= timeout_secs;
ab9a91eb05a54f7675e0bf861aca53f417e1980dTimo Sirainenstatic int file_lock_do(int fd, const char *path, int lock_type,
ab9a91eb05a54f7675e0bf861aca53f417e1980dTimo Sirainen unsigned int timeout_secs, const char **error_r)
c04f9a724a7b3cc649485a61b0a540868d25d71bTimo Sirainen lock_type_str = lock_type == F_UNLCK ? "unlock" :
eb0816090cf5a549280ad783b9aa6fec199d36baTimo Sirainen (lock_type == F_RDLCK ? "read-lock" : "write-lock");
cd83124e5d070a016c590bb0b1096d7828c7b6adTimo Sirainen "Can't lock file %s: fcntl() locks not supported", path);
6d931bbce16786df431e9ae8201a78a95084316dTimo Sirainen ret = fcntl(fd, timeout_secs != 0 ? F_SETLKW : F_SETLK, &fl);
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen /* locked by another process */
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen "fcntl(%s, %s, F_SETLK) locking failed: %m "
741d705983e10046f07ef372b760bcdd169b068aTimo Sirainen "(File is already locked)", path, lock_type_str);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (err_is_lock_timeout(started, timeout_secs)) {
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen "fcntl(%s, %s, F_SETLKW) locking failed: "
5da1aa5197a43d83f0fb3eeb83125c7cd73d1b62Timo Sirainen "Timed out after %u seconds%s",
case FILE_LOCK_METHOD_FLOCK: {
#ifndef HAVE_FLOCK
switch (lock_type) {
case F_RDLCK:
case F_WRLCK:
case F_UNLCK:
if (timeout_secs != 0) {
alarm(0);
if (ret == 0)
case FILE_LOCK_METHOD_DOTLOCK:
i_unreached();
unsigned int timeout_secs,
const char *error;
int ret;
if (ret < 0)
return ret;
unsigned int timeout_secs,
int ret;
if (ret <= 0)
return ret;
const char *error;
int ret;
if (ret <= 0)
return ret;
const char *error;
void file_lock_wait_start(void)
static void file_lock_wait_init_warning(void)
const char *value;
file_lock_slow_warning_usecs > 0) {
if (file_lock_slow_warning_usecs < 0)
if (file_lock_slow_warning_usecs < 0)
return file_lock_wait_usecs;