file-dotlock.c revision d2e0d8ee40bff599057a68c5b3e03604e9a22ccc
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen/* Copyright (C) 2003 Timo Sirainen */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen/* 0.1 .. 0.2msec */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
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/* 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 Sirainenstatic struct dotlock *
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenfile_dotlock_alloc(const struct dotlock_settings *settings)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen dotlock->settings.lock_suffix = DEFAULT_LOCK_SUFFIX;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic pid_t read_local_pid(const char *lock_path)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* read line */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* fix the string */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* it should contain pid:host */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* host must be ours */
c649139f889c02154fc9a153728b81619edb5663Timo Sirainenupdate_change_info(const struct stat *st, struct file_change_info *change,
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 /* 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 :
53ec1ff2231d477db3103c51987fa9cb6033bc16Timo Sirainenstatic int update_lock_info(time_t now, struct lock_info *lock_info,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (nfs_safe_lstat(lock_info->lock_path, &st) < 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("lstat(%s) failed: %m", lock_info->lock_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen *changed_r = update_change_info(&st, &lock_info->lock_info,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic int check_lock(time_t now, struct lock_info *lock_info)
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen time_t stale_timeout = lock_info->set->stale_timeout;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if ((ret = update_lock_info(now, lock_info, &changed)) != 0)
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 /* no pid checking */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* we just checked the pid */
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 /* we've local PID. Check if it exists. */
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 /* 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 /* still there, go ahead and override it */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* no change checking */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (now > lock_info->last_change + stale_timeout) {
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 /* file doesn't exist. treat it as if
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen it hasn't changed */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("stat(%s) failed: %m", lock_info->path);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen (void)update_change_info(&st, &lock_info->file_info,
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);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic int file_write_pid(int fd, const char *path)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen const char *str;
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* write our pid and host, if possible */
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen str = t_strdup_printf("%s:%s", my_pid, my_hostname);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen /* failed, leave it empty then */
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenstatic int try_create_lock_hardlink(struct lock_info *lock_info, bool write_pid,
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen const char *temp_prefix = lock_info->set->temp_prefix;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen const char *p;
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen /* we'll need our temp file first. */
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen /* add directory */
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen /* add directory */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (link(lock_info->temp_path, lock_info->lock_path) < 0) {
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("unlink(%s) failed: %m", lock_info->temp_path);
a0c453a8edaec90fb0d945c874de0b1845bc7d7eTimo Sirainen /* non-fatal, continue */
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenstatic int try_create_lock_excl(struct lock_info *lock_info, bool write_pid)
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen fd = open(lock_info->lock_path, O_RDWR | O_EXCL | O_CREAT, 0666);
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainen i_error("open(%s) failed: %m", lock_info->lock_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (file_write_pid(fd, lock_info->lock_path) < 0) {
ecdce39e5ef4b62eefa9f5818f17d153fd5d710aTimo Sirainenstatic void dotlock_wait_end(struct ioloop *ioloop)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic void dotlock_wait(struct lock_info *lock_info)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen switch (io_add_notify(lock_info->lock_path, dotlock_wait_end,
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen /* the lock file doesn't exist anymore, don't sleep */
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen /* listening for files not supported */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen to = timeout_add(LOCK_RANDOM_USLEEP_TIME/1000,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic int dotlock_create(const char *path, struct dotlock *dotlock,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen enum dotlock_create_flags flags, bool write_pid)
c649139f889c02154fc9a153728b81619edb5663Timo Sirainen const struct dotlock_settings *set = &dotlock->settings;
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 if ((flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen try_create_lock_hardlink(&lock_info, write_pid,
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen if (last_notify != now && set->callback != NULL) {
8a13d19a514bfc316149eda172558d12526f9e4eTimo Sirainen unsigned int secs_left =
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* we don't want to override */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen /* successful dotlock creation */
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_warning("Created dotlock file's timestamp is "
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen "different than current time "
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("unlink(%s) failed: %m", lock_info.temp_path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenstatic void file_dotlock_free(struct dotlock *dotlock)
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainen i_error("close(%s) failed: %m", dotlock->path);
48566ca412a7cf3b42512fd0ec112744778e5da0Timo Sirainenint file_dotlock_create(const struct dotlock_settings *set, const char *path,
const char *lock_path;
t_push();
t_pop();
return ret;
t_pop();
t_pop();
t_pop();
t_pop();
const char *lock_path;
int ret;
t_push();
t_pop();
if (ret <= 0) {
const char *lock_path;
int fd;
const char *lock_path;
int ret = 0;
t_push();
t_pop();
return ret;