file-dotlock.c revision d2e0d8ee40bff599057a68c5b3e03604e9a22ccc
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen/* Copyright (C) 2003 Timo Sirainen */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include "lib.h"
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include "ioloop.h"
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include "str.h"
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen#include "hex-binary.h"
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include "hostpid.h"
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include "randgen.h"
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include "write-full.h"
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include "safe-mkstemp.h"
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include "nfs-workarounds.h"
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include "file-dotlock.h"
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include <stdio.h>
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include <stdlib.h>
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include <signal.h>
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include <time.h>
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include <utime.h>
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#include <sys/stat.h>
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#define DEFAULT_LOCK_SUFFIX ".lock"
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen/* 0.1 .. 0.2msec */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen/* If the dotlock is newer than this, don't verify that the PID it contains
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen is valid (since it most likely is). */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#define STALE_PID_CHECK_SECS 2
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen/* Maximum difference between current time and create file's ctime before
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen logging a warning. Should be less than a second in normal operation. */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#define MAX_TIME_DIFF 30
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstruct dotlock {
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen struct dotlock_settings settings;
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen dev_t dev;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen ino_t ino;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen time_t mtime;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen char *path;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen char *lock_path;
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen int fd;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen time_t lock_time;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen};
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstruct file_change_info {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dev_t dev;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen ino_t ino;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen off_t size;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen time_t ctime, mtime;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen};
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstruct lock_info {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen const struct dotlock_settings *set;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen const char *path, *lock_path, *temp_path;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen int fd;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct file_change_info lock_info;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct file_change_info file_info;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen bool have_pid, use_io_notify;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen time_t last_pid_check;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen time_t last_change;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen};
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic struct dotlock *
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenfile_dotlock_alloc(const struct dotlock_settings *settings)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen{
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct dotlock *dotlock;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dotlock = i_new(struct dotlock, 1);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dotlock->settings = *settings;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (dotlock->settings.lock_suffix == NULL)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dotlock->settings.lock_suffix = DEFAULT_LOCK_SUFFIX;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dotlock->fd = -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return dotlock;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen}
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic pid_t read_local_pid(const char *lock_path)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen{
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen char buf[512], *host;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen int fd;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen ssize_t ret;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen fd = open(lock_path, O_RDONLY);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (fd == -1)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return -1; /* ignore the actual error */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* read line */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen ret = read(fd, buf, sizeof(buf)-1);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen (void)close(fd);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (ret <= 0)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* fix the string */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (buf[ret-1] == '\n')
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen ret--;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen buf[ret] = '\0';
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* it should contain pid:host */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen host = strchr(buf, ':');
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (host == NULL)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen *host++ = '\0';
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* host must be ours */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (strcmp(host, my_hostname) != 0)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (!is_numeric(buf, '\0'))
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return (pid_t)strtoul(buf, NULL, 0);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen}
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
c649139f889c02154fc9a153728b81619edb5663Timo Sirainenstatic bool
c649139f889c02154fc9a153728b81619edb5663Timo Sirainenupdate_change_info(const struct stat *st, struct file_change_info *change,
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen time_t *last_change_r, time_t now)
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen{
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen if (change->ino != st->st_ino || !CMP_DEV_T(change->dev, st->st_dev) ||
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen change->ctime != st->st_ctime || change->mtime != st->st_mtime ||
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen change->size != st->st_size) {
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen time_t change_time = now;
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen if (change->ctime == 0) {
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen /* First check, set last_change to file's change time.
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen Use mtime instead if it's higher, but only if it's
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen not higher than current time, because the mtime
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen can also be used for keeping metadata. */
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen change_time = st->st_mtime > now ? st->st_ctime :
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen I_MAX(st->st_ctime, st->st_mtime);
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen }
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen if (*last_change_r < change_time)
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen *last_change_r = change_time;
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen change->ino = st->st_ino;
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen change->dev = st->st_dev;
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen change->ctime = st->st_ctime;
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen change->mtime = st->st_mtime;
53ec1ff2231d477db3103c51987fa9cb6033bc16Timo Sirainen change->size = st->st_size;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return TRUE;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return FALSE;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen}
53ec1ff2231d477db3103c51987fa9cb6033bc16Timo Sirainen
53ec1ff2231d477db3103c51987fa9cb6033bc16Timo Sirainenstatic int update_lock_info(time_t now, struct lock_info *lock_info,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen bool *changed_r)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen{
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct stat st;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (nfs_safe_lstat(lock_info->lock_path, &st) < 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (errno != ENOENT) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("lstat(%s) failed: %m", lock_info->lock_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return 1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen *changed_r = update_change_info(&st, &lock_info->lock_info,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen &lock_info->last_change, now);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return 0;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen}
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic int check_lock(time_t now, struct lock_info *lock_info)
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen{
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen time_t stale_timeout = lock_info->set->stale_timeout;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen pid_t pid;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen bool changed;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen int ret;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if ((ret = update_lock_info(now, lock_info, &changed)) != 0)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return ret;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (changed) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* either our first check or someone else got the lock file.
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if the dotlock was created only a couple of seconds ago,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen don't bother to read its PID. */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen pid = lock_info->lock_info.mtime >=
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen now - STALE_PID_CHECK_SECS ? -1 :
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen read_local_pid(lock_info->lock_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen lock_info->have_pid = pid != -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen } else if (!lock_info->have_pid) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* no pid checking */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen pid = -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen } else {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (lock_info->last_pid_check == now) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* we just checked the pid */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return 0;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* re-read the pid. even if all times and inodes are the same,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen the PID in the file might have changed if lock files were
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen rapidly being recreated. */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen pid = read_local_pid(lock_info->lock_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen lock_info->have_pid = pid != -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (lock_info->have_pid) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* we've local PID. Check if it exists. */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (kill(pid, 0) == 0 || errno != ESRCH) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (pid != getpid())
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return 0;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* it's us. either we're locking it again, or it's a
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen stale lock file with same pid than us. either way,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen recreate it.. */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* doesn't exist - now check again if the dotlock was just
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen deleted or replaced */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if ((ret = update_lock_info(now, lock_info, &changed)) != 0)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return ret;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (!changed) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* still there, go ahead and override it */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (unlink(lock_info->lock_path) < 0 &&
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen errno != ENOENT) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("unlink(%s) failed: %m",
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen lock_info->lock_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return 1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (stale_timeout == 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* no change checking */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return 0;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (now > lock_info->last_change + stale_timeout) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct stat st;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* possibly stale lock file. check also the timestamp of the
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen file we're protecting. */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (nfs_safe_stat(lock_info->path, &st) < 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (errno == ENOENT) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* file doesn't exist. treat it as if
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen it hasn't changed */
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen } else {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("stat(%s) failed: %m", lock_info->path);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen return -1;
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen }
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen } else {
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen (void)update_change_info(&st, &lock_info->file_info,
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen &lock_info->last_change, now);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (now > lock_info->last_change + stale_timeout) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* no changes for a while, assume stale lock */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (unlink(lock_info->lock_path) < 0 && errno != ENOENT) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("unlink(%s) failed: %m", lock_info->lock_path);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen return -1;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen }
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen return 1;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return 0;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen}
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic int file_write_pid(int fd, const char *path)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen{
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen const char *str;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* write our pid and host, if possible */
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen str = t_strdup_printf("%s:%s", my_pid, my_hostname);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen if (write_full(fd, str, strlen(str)) < 0) {
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen /* failed, leave it empty then */
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen if (ftruncate(fd, 0) < 0) {
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen i_error("ftruncate(%s) failed: %m", path);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen return -1;
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen }
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen }
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen return 0;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen}
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenstatic int try_create_lock_hardlink(struct lock_info *lock_info, bool write_pid,
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen string_t *tmp_path)
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen{
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen const char *temp_prefix = lock_info->set->temp_prefix;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen const char *p;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (lock_info->temp_path == NULL) {
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen /* we'll need our temp file first. */
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen i_assert(lock_info->fd == -1);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen p = strrchr(lock_info->lock_path, '/');
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen str_truncate(tmp_path, 0);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen if (temp_prefix != NULL) {
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen if (*temp_prefix != '/' && p != NULL) {
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen /* add directory */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen str_append_n(tmp_path, lock_info->lock_path,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen p - lock_info->lock_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen str_append_c(tmp_path, '/');
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen str_append(tmp_path, temp_prefix);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen } else {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (p != NULL) {
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen /* add directory */
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen str_append_n(tmp_path, lock_info->lock_path,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen p - lock_info->lock_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen str_append_c(tmp_path, '/');
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen str_printfa(tmp_path, ".temp.%s.%s.",
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen my_hostname, my_pid);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen lock_info->fd = safe_mkstemp(tmp_path, 0666,
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen (uid_t)-1, (gid_t)-1);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (lock_info->fd == -1)
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen return -1;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (write_pid) {
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen if (file_write_pid(lock_info->fd,
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen str_c(tmp_path)) < 0) {
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen (void)close(lock_info->fd);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen lock_info->fd = -1;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen return -1;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen }
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen }
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen lock_info->temp_path = str_c(tmp_path);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (link(lock_info->temp_path, lock_info->lock_path) < 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (errno == EEXIST)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return 0;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("link(%s, %s) failed: %m",
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen lock_info->temp_path, lock_info->lock_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return -1;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (unlink(lock_info->temp_path) < 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("unlink(%s) failed: %m", lock_info->temp_path);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen /* non-fatal, continue */
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen }
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen lock_info->temp_path = NULL;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen return 1;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen}
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenstatic int try_create_lock_excl(struct lock_info *lock_info, bool write_pid)
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen{
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen int fd;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen fd = open(lock_info->lock_path, O_RDWR | O_EXCL | O_CREAT, 0666);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (fd == -1) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (errno == EEXIST)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return 0;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen i_error("open(%s) failed: %m", lock_info->lock_path);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen return -1;
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (write_pid) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (file_write_pid(fd, lock_info->lock_path) < 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen (void)close(fd);
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen return -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen lock_info->fd = fd;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return 1;
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen}
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenstatic void dotlock_wait_end(struct ioloop *ioloop)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen{
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen io_loop_stop(ioloop);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen}
6b09a3b269f4b10364c9a77f6614dbe3d306b79dTimo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic void dotlock_wait(struct lock_info *lock_info)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen{
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct ioloop *ioloop;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct io *io;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct timeout *to;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (!lock_info->use_io_notify) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen usleep(LOCK_RANDOM_USLEEP_TIME);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen ioloop = io_loop_create();
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen switch (io_add_notify(lock_info->lock_path, dotlock_wait_end,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen ioloop, &io)) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen case IO_NOTIFY_ADDED:
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen break;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen case IO_NOTIFY_NOTFOUND:
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen /* the lock file doesn't exist anymore, don't sleep */
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen io_loop_destroy(&ioloop);
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen return;
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen case IO_NOTIFY_DISABLED:
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen /* listening for files not supported */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen io_loop_destroy(&ioloop);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen lock_info->use_io_notify = FALSE;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen usleep(LOCK_RANDOM_USLEEP_TIME);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen to = timeout_add(LOCK_RANDOM_USLEEP_TIME/1000,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dotlock_wait_end, ioloop);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen io_loop_run(ioloop);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen io_remove(&io);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen timeout_remove(&to);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen io_loop_destroy(&ioloop);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen}
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic int dotlock_create(const char *path, struct dotlock *dotlock,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen enum dotlock_create_flags flags, bool write_pid)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen{
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen const struct dotlock_settings *set = &dotlock->settings;
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen const char *lock_path;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct lock_info lock_info;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct stat st;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen unsigned int stale_notify_threshold;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen unsigned int change_secs, wait_left;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen time_t now, max_wait_time, last_notify;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen string_t *tmp_path;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen int ret;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen bool do_wait;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen now = time(NULL);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen lock_path = t_strconcat(path, set->lock_suffix, NULL);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen stale_notify_threshold = set->stale_timeout / 2;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen max_wait_time = (flags & DOTLOCK_CREATE_FLAG_NONBLOCK) != 0 ? 0 :
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen now + set->timeout;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen tmp_path = t_str_new(256);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen memset(&lock_info, 0, sizeof(lock_info));
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen lock_info.path = path;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen lock_info.set = set;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen lock_info.lock_path = lock_path;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen lock_info.fd = -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen lock_info.use_io_notify = set->use_io_notify;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen last_notify = 0; do_wait = FALSE;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen do {
8a13d19a514bfc316149eda172558d12526f9e4eTimo Sirainen if (do_wait) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dotlock_wait(&lock_info);
8a13d19a514bfc316149eda172558d12526f9e4eTimo Sirainen do_wait = FALSE;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen ret = check_lock(now, &lock_info);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (ret < 0)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen break;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (ret == 1) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if ((flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen break;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen ret = set->use_excl_lock ?
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen try_create_lock_excl(&lock_info, write_pid) :
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen try_create_lock_hardlink(&lock_info, write_pid,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen tmp_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (ret != 0)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen break;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen do_wait = TRUE;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (last_notify != now && set->callback != NULL) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen last_notify = now;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen change_secs = now - lock_info.last_change;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen wait_left = max_wait_time - now;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
8a13d19a514bfc316149eda172558d12526f9e4eTimo Sirainen t_push();
8a13d19a514bfc316149eda172558d12526f9e4eTimo Sirainen if (change_secs >= stale_notify_threshold &&
8a13d19a514bfc316149eda172558d12526f9e4eTimo Sirainen change_secs <= wait_left) {
8a13d19a514bfc316149eda172558d12526f9e4eTimo Sirainen unsigned int secs_left =
8a13d19a514bfc316149eda172558d12526f9e4eTimo Sirainen set->stale_timeout < change_secs ?
8a13d19a514bfc316149eda172558d12526f9e4eTimo Sirainen 0 : set->stale_timeout - change_secs;
8a13d19a514bfc316149eda172558d12526f9e4eTimo Sirainen if (!set->callback(secs_left, TRUE,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen set->context)) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* we don't want to override */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen lock_info.last_change = now;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen } else {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen (void)set->callback(wait_left, FALSE,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen set->context);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen t_pop();
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen now = time(NULL);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen } while (now < max_wait_time);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (ret > 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (fstat(lock_info.fd, &st) < 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("fstat(%s) failed: %m", lock_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen ret = -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen } else {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* successful dotlock creation */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dotlock->dev = st.st_dev;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dotlock->ino = st.st_ino;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dotlock->path = i_strdup(path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dotlock->fd = lock_info.fd;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dotlock->lock_time = now;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen lock_info.fd = -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (st.st_ctime + MAX_TIME_DIFF < now ||
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen st.st_ctime - MAX_TIME_DIFF > now) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_warning("Created dotlock file's timestamp is "
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen "different than current time "
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen "(%s vs %s): %s", dec2str(st.st_ctime),
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dec2str(now), path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (lock_info.fd != -1) {
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen int old_errno = errno;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (close(lock_info.fd) < 0)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("close(%s) failed: %m", lock_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen errno = old_errno;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (lock_info.temp_path != NULL) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (unlink(lock_info.temp_path) < 0)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("unlink(%s) failed: %m", lock_info.temp_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (ret == 0)
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen errno = EAGAIN;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen return ret;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen}
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic void file_dotlock_free(struct dotlock *dotlock)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen{
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen int old_errno;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (dotlock->fd != -1) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen old_errno = errno;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (close(dotlock->fd) < 0)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("close(%s) failed: %m", dotlock->path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dotlock->fd = -1;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen errno = old_errno;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen }
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_free(dotlock->path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_free(dotlock->lock_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_free(dotlock);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen}
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenint file_dotlock_create(const struct dotlock_settings *set, const char *path,
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen enum dotlock_create_flags flags,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen struct dotlock **dotlock_r)
{
struct dotlock *dotlock;
const char *lock_path;
struct stat st;
int fd, ret;
*dotlock_r = NULL;
t_push();
dotlock = file_dotlock_alloc(set);
lock_path = t_strconcat(path, dotlock->settings.lock_suffix, NULL);
ret = dotlock_create(path, dotlock, flags, TRUE);
if (ret <= 0 || (flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0) {
file_dotlock_free(dotlock);
t_pop();
return ret;
}
fd = dotlock->fd;
dotlock->fd = -1;
if (close(fd) < 0) {
i_error("close(%s) failed: %m", lock_path);
file_dotlock_free(dotlock);
t_pop();
return -1;
}
/* With NFS the writes may have been flushed only when closing the
file. Get the mtime again after that to avoid "dotlock was modified"
errors. */
if (lstat(lock_path, &st) < 0) {
if (errno != ENOENT)
i_error("stat(%s) failed: %m", lock_path);
else {
i_error("dotlock %s was immediately deleted under us",
lock_path);
}
file_dotlock_free(dotlock);
t_pop();
return -1;
}
/* extra sanity check won't hurt.. */
if (st.st_dev != dotlock->dev || st.st_ino != dotlock->ino) {
i_error("dotlock %s was immediately recreated under us",
lock_path);
file_dotlock_free(dotlock);
t_pop();
return -1;
}
dotlock->mtime = st.st_mtime;
*dotlock_r = dotlock;
t_pop();
return 1;
}
int file_dotlock_delete(struct dotlock **dotlock_p)
{
struct dotlock *dotlock;
const char *lock_path;
struct stat st;
dotlock = *dotlock_p;
*dotlock_p = NULL;
lock_path = file_dotlock_get_lock_path(dotlock);
if (nfs_safe_lstat(lock_path, &st) < 0) {
if (errno == ENOENT) {
i_warning("Our dotlock file %s was deleted "
"(kept it %d secs)", lock_path,
(int)(time(NULL) - dotlock->lock_time));
file_dotlock_free(dotlock);
return 0;
}
i_error("lstat(%s) failed: %m", lock_path);
file_dotlock_free(dotlock);
return -1;
}
if (dotlock->ino != st.st_ino ||
!CMP_DEV_T(dotlock->dev, st.st_dev)) {
i_warning("Our dotlock file %s was overridden "
"(kept it %d secs)", lock_path,
(int)(dotlock->lock_time - time(NULL)));
errno = EEXIST;
file_dotlock_free(dotlock);
return 0;
}
if (dotlock->mtime != st.st_mtime && dotlock->fd == -1) {
i_warning("Our dotlock file %s was modified (%s vs %s), "
"assuming it wasn't overridden (kept it %d secs)",
lock_path,
dec2str(dotlock->mtime), dec2str(st.st_mtime),
(int)(time(NULL) - dotlock->lock_time));
}
if (unlink(lock_path) < 0) {
if (errno == ENOENT) {
i_warning("Our dotlock file %s was deleted "
"(kept it %d secs)", lock_path,
(int)(time(NULL) - dotlock->lock_time));
file_dotlock_free(dotlock);
return 0;
}
i_error("unlink(%s) failed: %m", lock_path);
file_dotlock_free(dotlock);
return -1;
}
file_dotlock_free(dotlock);
return 1;
}
int file_dotlock_open(const struct dotlock_settings *set, const char *path,
enum dotlock_create_flags flags,
struct dotlock **dotlock_r)
{
struct dotlock *dotlock;
int ret;
dotlock = file_dotlock_alloc(set);
t_push();
ret = dotlock_create(path, dotlock, flags, FALSE);
t_pop();
if (ret <= 0) {
file_dotlock_free(dotlock);
*dotlock_r = NULL;
return -1;
}
*dotlock_r = dotlock;
return dotlock->fd;
}
int file_dotlock_replace(struct dotlock **dotlock_p,
enum dotlock_replace_flags flags)
{
struct dotlock *dotlock;
struct stat st, st2;
const char *lock_path;
int fd;
dotlock = *dotlock_p;
*dotlock_p = NULL;
fd = dotlock->fd;
if ((flags & DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) != 0)
dotlock->fd = -1;
lock_path = file_dotlock_get_lock_path(dotlock);
if ((flags & DOTLOCK_REPLACE_FLAG_VERIFY_OWNER) != 0) {
if (fstat(fd, &st) < 0) {
i_error("fstat(%s) failed: %m", lock_path);
file_dotlock_free(dotlock);
return -1;
}
if (nfs_safe_lstat(lock_path, &st2) < 0) {
i_error("lstat(%s) failed: %m", lock_path);
file_dotlock_free(dotlock);
return -1;
}
if (st.st_ino != st2.st_ino ||
!CMP_DEV_T(st.st_dev, st2.st_dev)) {
i_warning("Our dotlock file %s was overridden "
"(kept it %d secs)", lock_path,
(int)(time(NULL) - dotlock->lock_time));
errno = EEXIST;
file_dotlock_free(dotlock);
return 0;
}
}
if (rename(lock_path, dotlock->path) < 0) {
i_error("rename(%s, %s) failed: %m", lock_path, dotlock->path);
file_dotlock_free(dotlock);
return -1;
}
file_dotlock_free(dotlock);
return 1;
}
int file_dotlock_touch(struct dotlock *dotlock)
{
time_t now = time(NULL);
struct utimbuf buf;
const char *lock_path;
int ret = 0;
if (dotlock->mtime == now)
return 0;
dotlock->mtime = now;
buf.actime = buf.modtime = now;
t_push();
lock_path = file_dotlock_get_lock_path(dotlock);
if (utime(lock_path, &buf) < 0) {
i_error("utime(%s) failed: %m", lock_path);
ret = -1;
}
t_pop();
return ret;
}
const char *file_dotlock_get_lock_path(struct dotlock *dotlock)
{
if (dotlock->lock_path == NULL) {
dotlock->lock_path =
i_strconcat(dotlock->path,
dotlock->settings.lock_suffix, NULL);
}
return dotlock->lock_path;
}