file-lock.c revision 1ea0aa8e14e843f2776746776a429b0a1aae299d
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic long long file_lock_slow_warning_usecs = -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic void file_lock_log_warning_if_slow(struct file_lock *lock);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenbool file_lock_method_parse(const char *name, enum file_lock_method *method_r)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenconst char *file_lock_method_to_str(enum file_lock_method method)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return "fcntl";
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return "flock";
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return "dotlock";
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenint file_try_lock(int fd, const char *path, int lock_type,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return file_wait_lock(fd, path, lock_type, lock_method, 0, lock_r);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenint file_try_lock_error(int fd, const char *path, int lock_type,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct file_lock **lock_r, const char **error_r)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return file_wait_lock_error(fd, path, lock_type, lock_method, 0,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic const char *
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenfile_lock_find_fcntl(int lock_fd, int lock_type)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen fl.l_type == F_UNLCK || fl.l_pid == -1 || fl.l_pid == 0)
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 Sirainenstatic const char *
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenfile_lock_find_proc_locks(int lock_fd ATTR_UNUSED)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* do anything except Linux support this? don't bother trying it for
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen OSes we don't know about. */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_snprintf(node_buf, sizeof(node_buf), "%02x:%02x:%llu",
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 /* number: FLOCK/POSIX ADVISORY READ/WRITE pid
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen major:minor:inode region-start region-end */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ; /* don't continue from within a T_BEGIN {...} T_END */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* not found */
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 Sirainenconst char *file_lock_find(int lock_fd, enum file_lock_method lock_method,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *ret;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ret = file_lock_find_fcntl(lock_fd, lock_type);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic bool err_is_lock_timeout(time_t started, unsigned int timeout_secs)
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 (unsigned long)(time(NULL) - started + 1) >= timeout_secs;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic int file_lock_do(int fd, const char *path, int lock_type,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen unsigned int timeout_secs, const char **error_r)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen lock_type_str = lock_type == F_UNLCK ? "unlock" :
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (lock_type == F_RDLCK ? "read-lock" : "write-lock");
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "Can't lock file %s: fcntl() locks not supported", path);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen ret = fcntl(fd, timeout_secs != 0 ? F_SETLKW : F_SETLK, &fl);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* locked by another process */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "fcntl(%s, %s, F_SETLK) locking failed: %m "
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "(File is already locked)", path, lock_type_str);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (err_is_lock_timeout(started, timeout_secs)) {
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen "fcntl(%s, %s, F_SETLKW) locking failed: "
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "Timed out after %u seconds%s",
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 i_panic("%s%s", *error_r, file_lock_find(fd, lock_method, lock_type));
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "Can't lock file %s: flock() not supported", path);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen int operation = timeout_secs != 0 ? 0 : LOCK_NB;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen if (timeout_secs == 0 && errno == EWOULDBLOCK) {
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen /* locked by another process */
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen "flock(%s, %s) failed: %m "
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen "(File is already locked)", path, lock_type_str);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (err_is_lock_timeout(started, timeout_secs)) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen *error_r = t_strdup_printf("flock(%s, %s) failed: "
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "Timed out after %u seconds%s",
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *error_r = t_strdup_printf("flock(%s, %s) failed: %m",
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_panic("%s%s", *error_r, file_lock_find(fd, lock_method, lock_type));
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* we shouldn't get here */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenint file_wait_lock(int fd, const char *path, int lock_type,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen ret = file_wait_lock_error(fd, path, lock_type, lock_method,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenint file_wait_lock_error(int fd, const char *path, int lock_type,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct file_lock **lock_r, const char **error_r)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ret = file_lock_do(fd, path, lock_type, lock_method, timeout_secs, error_r);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (gettimeofday(&lock->locked_time, NULL) < 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenint file_lock_try_update(struct file_lock *lock, int lock_type)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen ret = file_lock_do(lock->fd, lock->path, lock_type,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (file_lock_do(lock->fd, lock->path, F_UNLCK,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* this shouldn't happen */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_error("file_unlock(%s) failed: %m", lock->path);
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;