Skip to content

Commit

Permalink
lib: file_lock_set_unlink_on_free() - Avoid unlink() if another proce…
Browse files Browse the repository at this point in the history
…ss is waiting on the lock
  • Loading branch information
sirainen committed Sep 19, 2017
1 parent c27f060 commit 6a6bda3
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 11 deletions.
7 changes: 6 additions & 1 deletion src/lib/file-create-locked.h
Expand Up @@ -33,7 +33,12 @@ struct file_create_settings {
If link() fails, opening is retried again. Returns fd on success,
-1 on error. errno is preserved for the last failed syscall, so most
importantly ENOENT could mean that the directory doesn't exist and EAGAIN
means locking timed out. */
means locking timed out.
If this function is used to create lock files, file_lock_set_unlink_on_free()
should be used for the resulting lock. It attempts to avoid unlinking the
file if there are already other processes using the lock. That can help to
avoid "Creating a locked file ... keeps failing" errors */
int file_create_locked(const char *path, const struct file_create_settings *set,
struct file_lock **lock_r, bool *created_r,
const char **error_r);
Expand Down
53 changes: 45 additions & 8 deletions src/lib/file-lock.c
Expand Up @@ -352,10 +352,20 @@ void file_lock_set_close_on_free(struct file_lock *lock, bool set)
lock->close_on_free = set;
}

static void file_unlock_real(struct file_lock *lock)
{
const char *error;

if (file_lock_do(lock->fd, lock->path, F_UNLCK,
lock->lock_method, 0, &error) == 0) {
/* this shouldn't happen */
i_error("file_unlock(%s) failed: %m", lock->path);
}
}

void file_unlock(struct file_lock **_lock)
{
struct file_lock *lock = *_lock;
const char *error;

*_lock = NULL;

Expand All @@ -364,23 +374,50 @@ void file_unlock(struct file_lock **_lock)
could be deleting the new lock. */
i_assert(!lock->unlink_on_free);

if (file_lock_do(lock->fd, lock->path, F_UNLCK,
lock->lock_method, 0, &error) == 0) {
/* this shouldn't happen */
i_error("file_unlock(%s) failed: %m", lock->path);
}

file_unlock_real(lock);
file_lock_free(&lock);
}

static void file_try_unlink_locked(struct file_lock *lock)
{
struct file_lock *temp_lock = NULL;
struct stat st1, st2;
const char *error;
int ret;

file_unlock_real(lock);
ret = file_try_lock_error(lock->fd, lock->path, F_WRLCK,
lock->lock_method, &temp_lock, &error);
if (ret < 0) {
i_error("file_lock_free(): Unexpectedly failed to retry locking %s: %s",
lock->path, error);
} else if (ret == 0) {
/* already locked by someone else */
} else if (fstat(lock->fd, &st1) < 0) {
/* not expected to happen */
i_error("file_lock_free(): fstat(%s) failed: %m", lock->path);
} else if (stat(lock->path, &st2) < 0) {
if (errno != ENOENT)
i_error("file_lock_free(): stat(%s) failed: %m", lock->path);
} else if (st1.st_ino != st2.st_ino ||
!CMP_DEV_T(st1.st_dev, st2.st_dev)) {
/* lock file was recreated already - don't delete it */
} else {
/* nobody was waiting on the lock - unlink it */
i_unlink(lock->path);
}
if (temp_lock != NULL)
file_lock_free(&temp_lock);
}

void file_lock_free(struct file_lock **_lock)
{
struct file_lock *lock = *_lock;

*_lock = NULL;

if (lock->unlink_on_free)
i_unlink(lock->path);
file_try_unlink_locked(lock);
if (lock->close_on_free)
i_close_fd(&lock->fd);

Expand Down
5 changes: 3 additions & 2 deletions src/lib/file-lock.h
Expand Up @@ -45,8 +45,9 @@ int file_wait_lock_error(int fd, const char *path, int lock_type,
/* Change the lock type. WARNING: This isn't an atomic operation!
The result is the same as file_unlock() + file_try_lock(). */
int file_lock_try_update(struct file_lock *lock, int lock_type);
/* When the lock is freed, unlink() the file automatically. This can be useful
for files that are only created to exist as lock files. */
/* When the lock is freed, unlink() the file automatically, unless other
processes are already waiting on the lock. This can be useful for files that
are only created to exist as lock files. */
void file_lock_set_unlink_on_free(struct file_lock *lock, bool set);
/* When the lock is freed, close the fd automatically. This can
be useful for files that are only created to exist as lock files. */
Expand Down

0 comments on commit 6a6bda3

Please sign in to comment.