file-dotlock.c revision fdcb22a688c4676face8db865736b217d9c07d19
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (C) 2003 Timo Sirainen */
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen/* 0.1 .. 0.2msec */
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen#define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen/* If the dotlock is newer than this, don't verify that the PID it contains
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen is valid (since it most likely is). */
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen/* Maximum difference between current time and create file's ctime before
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen logging a warning. Should be less than a second in normal operation. */
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainenstatic struct dotlock *
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainenfile_dotlock_alloc(const struct dotlock_settings *settings)
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen dotlock->settings.lock_suffix = DEFAULT_LOCK_SUFFIX;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainenstatic pid_t read_local_pid(const char *lock_path)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen /* read line */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* fix the string */
178511b57faa7c3f8203dd8b7e4059d00cbfc23aTimo Sirainen /* it should contain pid:host */
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen /* host must be ours */
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainenupdate_change_info(const struct stat *st, struct file_change_info *change,
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (change->ino != st->st_ino || !CMP_DEV_T(change->dev, st->st_dev) ||
268fe0fe3e748ae313e08b0918aa114815cf9d7cTimo Sirainen change->ctime != st->st_ctime || change->mtime != st->st_mtime ||
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen /* First check, set last_change to file's change time.
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen Use mtime instead if it's higher, but only if it's
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen not higher than current time, because the mtime
b9ac6179d3aee0d1641a4ee1d78da28628929c61Timo Sirainen can also be used for keeping metadata. */
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen change_time = st->st_mtime > now ? st->st_ctime :
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainenstatic int update_lock_info(time_t now, struct lock_info *lock_info,
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen i_error("lstat(%s) failed: %m", lock_info->lock_path);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen *changed_r = update_change_info(&st, &lock_info->lock_info,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic int check_lock(time_t now, struct lock_info *lock_info)
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen time_t stale_timeout = lock_info->set->stale_timeout;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if ((ret = update_lock_info(now, lock_info, &changed)) != 0)
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen /* either our first check or someone else got the lock file.
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen if the dotlock was created only a couple of seconds ago,
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen don't bother to read its PID. */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* no pid checking */
36e2fa21c22452470c1509cc63de20f7415c7b5eTimo Sirainen /* we just checked the pid */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* re-read the pid. even if all times and inodes are the same,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen the PID in the file might have changed if lock files were
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen rapidly being recreated. */
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen /* we've local PID. Check if it exists. */
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen /* it's us. either we're locking it again, or it's a
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen stale lock file with same pid than us. either way,
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen recreate it.. */
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen /* doesn't exist - now check again if the dotlock was just
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen deleted or replaced */
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen if ((ret = update_lock_info(now, lock_info, &changed)) != 0)
39775ad03c459efe64cce924658da5094ba417e1Timo Sirainen /* still there, go ahead and override it */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* no change checking */
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen if (now > lock_info->last_change + stale_timeout) {
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen /* possibly stale lock file. check also the timestamp of the
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen file we're protecting. */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* file doesn't exist. treat it as if
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen it hasn't changed */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_error("stat(%s) failed: %m", lock_info->path);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen (void)update_change_info(&st, &lock_info->file_info,
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen if (now > lock_info->last_change + stale_timeout) {
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen /* no changes for a while, assume stale lock */
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen if (unlink(lock_info->lock_path) < 0 && errno != ENOENT) {
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen i_error("unlink(%s) failed: %m", lock_info->lock_path);
484efa22e65c509f787dbbc892351146c726c257Timo Sirainenstatic int file_write_pid(int fd, const char *path)
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen const char *str;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* write our pid and host, if possible */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen str = t_strdup_printf("%s:%s", my_pid, my_hostname);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* failed, leave it empty then */
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainenstatic int try_create_lock_hardlink(struct lock_info *lock_info, bool write_pid,
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen const char *temp_prefix = lock_info->set->temp_prefix;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen const char *p;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen /* we'll need our temp file first. */
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen /* add directory */
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen /* add directory */
4d25408732be27e91f0430f71e87242760c2517cTimo Sirainen if (link(lock_info->temp_path, lock_info->lock_path) < 0) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_error("unlink(%s) failed: %m", lock_info->temp_path);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* non-fatal, continue */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic int try_create_lock_excl(struct lock_info *lock_info, bool write_pid)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen fd = open(lock_info->lock_path, O_RDWR | O_EXCL | O_CREAT, 0666);
b9ac6179d3aee0d1641a4ee1d78da28628929c61Timo Sirainen i_error("open(%s) failed: %m", lock_info->lock_path);
6e71799ee07bfd2289beae77c9bb5d7f1d30dccaTimo Sirainen if (file_write_pid(fd, lock_info->lock_path) < 0) {
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainenstatic void dotlock_wait_end(struct ioloop *ioloop)
77af8c68c416179e717fc2d551f72ec50b499c13Timo Sirainenstatic void dotlock_wait(struct lock_info *lock_info)
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen switch (io_add_notify(lock_info->lock_path, dotlock_wait_end,
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen /* the lock file doesn't exist anymore, don't sleep */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen /* listening for files not supported */
88553367d677170a4b703b9d52aac9eabf91c656Timo Sirainen to = timeout_add(LOCK_RANDOM_USLEEP_TIME/1000,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic int dotlock_create(const char *path, struct dotlock *dotlock,
3023fb352cbc2052b156f6d325c2629531a1b5b4Timo Sirainen enum dotlock_create_flags flags, bool write_pid)
3023fb352cbc2052b156f6d325c2629531a1b5b4Timo Sirainen const struct dotlock_settings *set = &dotlock->settings;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen lock_path = t_strconcat(path, set->lock_suffix, NULL);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen stale_notify_threshold = set->stale_timeout / 2;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen max_wait_time = (flags & DOTLOCK_CREATE_FLAG_NONBLOCK) != 0 ? 0 :
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if ((flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen try_create_lock_hardlink(&lock_info, write_pid,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (last_notify != now && set->callback != NULL) {
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen unsigned int secs_left =
7797aa2479e99aeb71057b7a2584b2cb72e4d3f8Timo Sirainen /* we don't want to override */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* successful dotlock creation */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_warning("Created dotlock file's timestamp is "
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen "different than current time "
7fd72a47d7ddfbd38c8697e228b6951f495dfb61Timo Sirainen i_error("unlink(%s) failed: %m", lock_info.temp_path);
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainenstatic void file_dotlock_free(struct dotlock *dotlock)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_error("close(%s) failed: %m", dotlock->path);
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainenint file_dotlock_create(const struct dotlock_settings *set, const char *path,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen lock_path = t_strconcat(path, dotlock->settings.lock_suffix, NULL);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen ret = dotlock_create(path, dotlock, flags, TRUE);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (ret <= 0 || (flags & DOTLOCK_CREATE_FLAG_CHECKONLY) != 0) {
36816b5af1472ae76a1909ae3cf29fd614b2ebfcTimo Sirainen /* some NFS implementations may have used cached mtime in previous
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen fstat() call. Check again to avoid "dotlock was modified" errors. */
8d5991f5c4a8840bf1ea754093dbec505564ab78Timo Sirainen i_error("dotlock %s was immediately deleted under us",
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* extra sanity check won't hurt.. */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (st.st_dev != dotlock->dev || st.st_ino != dotlock->ino) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_error("dotlock %s was immediately recreated under us",
dc8552739fa29f011ab71ec383ec6d580a5a9661Timo Sirainenint file_dotlock_delete(struct dotlock **dotlock_p)
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen lock_path = file_dotlock_get_lock_path(dotlock);
829c036d4ddfbd9ea49bd8a7c54e3057177d346eTimo Sirainen i_warning("Our dotlock file %s was overridden "
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen if (dotlock->mtime != st.st_mtime && dotlock->fd == -1) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_warning("Our dotlock file %s was modified (%s vs %s), "
7a6b45405fb1544ac476e6eb1402a70cc1ddcdcfTimo Sirainen "assuming it wasn't overridden (kept it %d secs)",
6a1e4eb2c6a267bec1e8704ce9137bebb7792702Timo Sirainen dec2str(dotlock->mtime), dec2str(st.st_mtime),
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenint file_dotlock_open(const struct dotlock_settings *set, const char *path,
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen ret = dotlock_create(path, dotlock, flags, FALSE);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenint file_dotlock_replace(struct dotlock **dotlock_p,
319944c0f35b311c998854e96d6463a084fd90aeTimo Sirainen if ((flags & DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) != 0)
319944c0f35b311c998854e96d6463a084fd90aeTimo Sirainen lock_path = file_dotlock_get_lock_path(dotlock);
319944c0f35b311c998854e96d6463a084fd90aeTimo Sirainen if ((flags & DOTLOCK_REPLACE_FLAG_VERIFY_OWNER) != 0) {
755fe6da51ab7f54aa1d86913cb344bffef60e79Timo Sirainen i_warning("Our dotlock file %s was overridden "
a10e5606a9e93f49cf13b3a35c8dc3f5d6ab5909Timo Sirainen i_error("rename(%s, %s) failed: %m", lock_path, dotlock->path);
b225c3c65f360d7b833f09f9b2fb3035ed5ea600Timo Sirainenint file_dotlock_touch(struct dotlock *dotlock)
a10e5606a9e93f49cf13b3a35c8dc3f5d6ab5909Timo Sirainen lock_path = file_dotlock_get_lock_path(dotlock);