"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/lib/file-lock.c" between
dovecot-2.3.16.tar.gz and dovecot-2.3.17.tar.gz

About: Dovecot is an IMAP and POP3 server, written with security primarily in mind.

file-lock.c  (dovecot-2.3.16):file-lock.c  (dovecot-2.3.17)
skipping to change at line 16 skipping to change at line 16
#include "file-dotlock.h" #include "file-dotlock.h"
#include "time-util.h" #include "time-util.h"
#include <time.h> #include <time.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifdef HAVE_FLOCK #ifdef HAVE_FLOCK
# include <sys/file.h> # include <sys/file.h>
#endif #endif
struct file_lock { struct file_lock {
struct file_lock_settings set;
int fd; int fd;
char *path; char *path;
struct dotlock *dotlock; struct dotlock *dotlock;
struct timeval locked_time; struct timeval locked_time;
int lock_type; int lock_type;
enum file_lock_method lock_method;
bool unlink_on_free;
bool close_on_free;
}; };
static struct timeval lock_wait_start; static struct timeval lock_wait_start;
static uint64_t file_lock_wait_usecs = 0; static uint64_t file_lock_wait_usecs = 0;
static long long file_lock_slow_warning_usecs = -1; static long long file_lock_slow_warning_usecs = -1;
static void file_lock_log_warning_if_slow(struct file_lock *lock); static void file_lock_log_warning_if_slow(struct file_lock *lock);
bool file_lock_method_parse(const char *name, enum file_lock_method *method_r) bool file_lock_method_parse(const char *name, enum file_lock_method *method_r)
{ {
skipping to change at line 60 skipping to change at line 59
return "fcntl"; return "fcntl";
case FILE_LOCK_METHOD_FLOCK: case FILE_LOCK_METHOD_FLOCK:
return "flock"; return "flock";
case FILE_LOCK_METHOD_DOTLOCK: case FILE_LOCK_METHOD_DOTLOCK:
return "dotlock"; return "dotlock";
} }
i_unreached(); i_unreached();
} }
int file_try_lock(int fd, const char *path, int lock_type, int file_try_lock(int fd, const char *path, int lock_type,
enum file_lock_method lock_method, const struct file_lock_settings *set,
struct file_lock **lock_r) struct file_lock **lock_r, const char **error_r)
{
return file_wait_lock(fd, path, lock_type, lock_method, 0, lock_r);
}
int file_try_lock_error(int fd, const char *path, int lock_type,
enum file_lock_method lock_method,
struct file_lock **lock_r, const char **error_r)
{ {
return file_wait_lock_error(fd, path, lock_type, lock_method, 0, return file_wait_lock(fd, path, lock_type, set, 0, lock_r, error_r);
lock_r, error_r);
} }
static const char * static const char *
file_lock_find_fcntl(int lock_fd, int lock_type) file_lock_find_fcntl(int lock_fd, int lock_type)
{ {
struct flock fl; struct flock fl;
i_zero(&fl); i_zero(&fl);
fl.l_type = lock_type; fl.l_type = lock_type;
fl.l_whence = SEEK_SET; fl.l_whence = SEEK_SET;
skipping to change at line 171 skipping to change at line 162
{ {
/* if EINTR took at least timeout_secs-1 number of seconds, /* if EINTR took at least timeout_secs-1 number of seconds,
assume it was the alarm. otherwise log EINTR failure. assume it was the alarm. otherwise log EINTR failure.
(We most likely don't want to retry EINTR since a signal (We most likely don't want to retry EINTR since a signal
means somebody wants us to stop blocking). */ means somebody wants us to stop blocking). */
return errno == EINTR && return errno == EINTR &&
(unsigned long)(time(NULL) - started + 1) >= timeout_secs; (unsigned long)(time(NULL) - started + 1) >= timeout_secs;
} }
static int file_lock_do(int fd, const char *path, int lock_type, static int file_lock_do(int fd, const char *path, int lock_type,
enum file_lock_method lock_method, const struct file_lock_settings *set,
unsigned int timeout_secs, const char **error_r) unsigned int timeout_secs, const char **error_r)
{ {
const char *lock_type_str; const char *lock_type_str;
time_t started = time(NULL); time_t started = time(NULL);
int ret; int ret;
i_assert(fd != -1); i_assert(fd != -1);
if (timeout_secs != 0) { if (timeout_secs != 0) {
alarm(timeout_secs); alarm(timeout_secs);
file_lock_wait_start(); file_lock_wait_start();
} }
lock_type_str = lock_type == F_UNLCK ? "unlock" : lock_type_str = lock_type == F_UNLCK ? "unlock" :
(lock_type == F_RDLCK ? "read-lock" : "write-lock"); (lock_type == F_RDLCK ? "read-lock" : "write-lock");
switch (lock_method) { switch (set->lock_method) {
case FILE_LOCK_METHOD_FCNTL: { case FILE_LOCK_METHOD_FCNTL: {
#ifndef HAVE_FCNTL #ifndef HAVE_FCNTL
*error_r = t_strdup_printf( *error_r = t_strdup_printf(
"Can't lock file %s: fcntl() locks not supported", path); "Can't lock file %s: fcntl() locks not supported", path);
return -1; return -1;
#else #else
struct flock fl; struct flock fl;
fl.l_type = lock_type; fl.l_type = lock_type;
fl.l_whence = SEEK_SET; fl.l_whence = SEEK_SET;
skipping to change at line 226 skipping to change at line 217
"(File is already locked)", path, lock_type_str); "(File is already locked)", path, lock_type_str);
return 0; return 0;
} }
if (err_is_lock_timeout(started, timeout_secs)) { if (err_is_lock_timeout(started, timeout_secs)) {
errno = EAGAIN; errno = EAGAIN;
*error_r = t_strdup_printf( *error_r = t_strdup_printf(
"fcntl(%s, %s, F_SETLKW) locking failed: " "fcntl(%s, %s, F_SETLKW) locking failed: "
"Timed out after %u seconds%s", "Timed out after %u seconds%s",
path, lock_type_str, timeout_secs, path, lock_type_str, timeout_secs,
file_lock_find(fd, lock_method, lock_type)); file_lock_find(fd, set->lock_method,
lock_type));
return 0; return 0;
} }
*error_r = t_strdup_printf("fcntl(%s, %s, %s) locking failed: %m" , *error_r = t_strdup_printf("fcntl(%s, %s, %s) locking failed: %m" ,
path, lock_type_str, timeout_secs == 0 ? "F_SETLK" : "F_S ETLKW"); path, lock_type_str, timeout_secs == 0 ? "F_SETLK" : "F_S ETLKW");
if (errno == EDEADLK) if (errno == EDEADLK && !set->allow_deadlock) {
i_panic("%s%s", *error_r, file_lock_find(fd, lock_method, i_panic("%s%s", *error_r,
lock_type)); file_lock_find(fd, set->lock_method,
lock_type));
}
return -1; return -1;
#endif #endif
} }
case FILE_LOCK_METHOD_FLOCK: { case FILE_LOCK_METHOD_FLOCK: {
#ifndef HAVE_FLOCK #ifndef HAVE_FLOCK
*error_r = t_strdup_printf( *error_r = t_strdup_printf(
"Can't lock file %s: flock() not supported", path); "Can't lock file %s: flock() not supported", path);
return -1; return -1;
#else #else
int operation = timeout_secs != 0 ? 0 : LOCK_NB; int operation = timeout_secs != 0 ? 0 : LOCK_NB;
skipping to change at line 277 skipping to change at line 272
*error_r = t_strdup_printf( *error_r = t_strdup_printf(
"flock(%s, %s) failed: %m " "flock(%s, %s) failed: %m "
"(File is already locked)", path, lock_type_str); "(File is already locked)", path, lock_type_str);
return 0; return 0;
} }
if (err_is_lock_timeout(started, timeout_secs)) { if (err_is_lock_timeout(started, timeout_secs)) {
errno = EAGAIN; errno = EAGAIN;
*error_r = t_strdup_printf("flock(%s, %s) failed: " *error_r = t_strdup_printf("flock(%s, %s) failed: "
"Timed out after %u seconds%s", "Timed out after %u seconds%s",
path, lock_type_str, timeout_secs, path, lock_type_str, timeout_secs,
file_lock_find(fd, lock_method, lock_type)); file_lock_find(fd, set->lock_method,
lock_type));
return 0; return 0;
} }
*error_r = t_strdup_printf("flock(%s, %s) failed: %m", *error_r = t_strdup_printf("flock(%s, %s) failed: %m",
path, lock_type_str); path, lock_type_str);
if (errno == EDEADLK) if (errno == EDEADLK && !set->allow_deadlock) {
i_panic("%s%s", *error_r, file_lock_find(fd, lock_method, i_panic("%s%s", *error_r,
lock_type)); file_lock_find(fd, set->lock_method,
lock_type));
}
return -1; return -1;
#endif #endif
} }
case FILE_LOCK_METHOD_DOTLOCK: case FILE_LOCK_METHOD_DOTLOCK:
/* we shouldn't get here */ /* we shouldn't get here */
i_unreached(); i_unreached();
} }
return 1; return 1;
} }
int file_wait_lock(int fd, const char *path, int lock_type, int file_wait_lock(int fd, const char *path, int lock_type,
enum file_lock_method lock_method, const struct file_lock_settings *set,
unsigned int timeout_secs, unsigned int timeout_secs,
struct file_lock **lock_r) struct file_lock **lock_r, const char **error_r)
{
const char *error;
int ret;
ret = file_wait_lock_error(fd, path, lock_type, lock_method,
timeout_secs, lock_r, &error);
if (ret < 0)
i_error("%s", error);
return ret;
}
int file_wait_lock_error(int fd, const char *path, int lock_type,
enum file_lock_method lock_method,
unsigned int timeout_secs,
struct file_lock **lock_r, const char **error_r)
{ {
struct file_lock *lock; struct file_lock *lock;
int ret; int ret;
ret = file_lock_do(fd, path, lock_type, lock_method, timeout_secs, error_ r); ret = file_lock_do(fd, path, lock_type, set, timeout_secs, error_r);
if (ret <= 0) if (ret <= 0)
return ret; return ret;
lock = i_new(struct file_lock, 1); lock = i_new(struct file_lock, 1);
lock->set = *set;
lock->fd = fd; lock->fd = fd;
lock->path = i_strdup(path); lock->path = i_strdup(path);
lock->lock_type = lock_type; lock->lock_type = lock_type;
lock->lock_method = lock_method;
i_gettimeofday(&lock->locked_time); i_gettimeofday(&lock->locked_time);
*lock_r = lock; *lock_r = lock;
return 1; return 1;
} }
int file_lock_try_update(struct file_lock *lock, int lock_type) int file_lock_try_update(struct file_lock *lock, int lock_type)
{ {
const char *error; const char *error;
int ret; int ret;
ret = file_lock_do(lock->fd, lock->path, lock_type, ret = file_lock_do(lock->fd, lock->path, lock_type, &lock->set, 0,
lock->lock_method, 0, &error); &error);
if (ret <= 0) if (ret <= 0)
return ret; return ret;
file_lock_log_warning_if_slow(lock); file_lock_log_warning_if_slow(lock);
lock->lock_type = lock_type; lock->lock_type = lock_type;
return 1; return 1;
} }
void file_lock_set_unlink_on_free(struct file_lock *lock, bool set) void file_lock_set_unlink_on_free(struct file_lock *lock, bool set)
{ {
lock->unlink_on_free = set; lock->set.unlink_on_free = set;
} }
void file_lock_set_close_on_free(struct file_lock *lock, bool set) void file_lock_set_close_on_free(struct file_lock *lock, bool set)
{ {
lock->close_on_free = set; lock->set.close_on_free = set;
} }
struct file_lock *file_lock_from_dotlock(struct dotlock **dotlock) struct file_lock *file_lock_from_dotlock(struct dotlock **dotlock)
{ {
struct file_lock *lock; struct file_lock *lock;
lock = i_new(struct file_lock, 1); lock = i_new(struct file_lock, 1);
lock->set.lock_method = FILE_LOCK_METHOD_DOTLOCK;
lock->fd = -1; lock->fd = -1;
lock->path = i_strdup(file_dotlock_get_lock_path(*dotlock)); lock->path = i_strdup(file_dotlock_get_lock_path(*dotlock));
lock->lock_type = F_WRLCK; lock->lock_type = F_WRLCK;
lock->lock_method = FILE_LOCK_METHOD_DOTLOCK;
i_gettimeofday(&lock->locked_time); i_gettimeofday(&lock->locked_time);
lock->dotlock = *dotlock; lock->dotlock = *dotlock;
*dotlock = NULL; *dotlock = NULL;
return lock; return lock;
} }
static void file_unlock_real(struct file_lock *lock) static void file_unlock_real(struct file_lock *lock)
{ {
const char *error; const char *error;
if (file_lock_do(lock->fd, lock->path, F_UNLCK, if (file_lock_do(lock->fd, lock->path, F_UNLCK, &lock->set, 0,
lock->lock_method, 0, &error) == 0) { &error) == 0) {
/* this shouldn't happen */ /* this shouldn't happen */
i_error("file_unlock(%s) failed: %m", lock->path); i_error("file_unlock(%s) failed: %m", lock->path);
} }
} }
void file_unlock(struct file_lock **_lock) void file_unlock(struct file_lock **_lock)
{ {
struct file_lock *lock = *_lock; struct file_lock *lock = *_lock;
*_lock = NULL; *_lock = NULL;
/* unlocking is unnecessary when the file is unlinked. or alternatively /* unlocking is unnecessary when the file is unlinked. or alternatively
the unlink() must be done before unlocking, because otherwise it the unlink() must be done before unlocking, because otherwise it
could be deleting the new lock. */ could be deleting the new lock. */
i_assert(!lock->unlink_on_free); i_assert(!lock->set.unlink_on_free);
if (lock->dotlock == NULL) if (lock->dotlock == NULL)
file_unlock_real(lock); file_unlock_real(lock);
file_lock_free(&lock); file_lock_free(&lock);
} }
static void file_try_unlink_locked(struct file_lock *lock) static void file_try_unlink_locked(struct file_lock *lock)
{ {
struct file_lock *temp_lock = NULL; struct file_lock *temp_lock = NULL;
struct file_lock_settings temp_set = lock->set;
struct stat st1, st2; struct stat st1, st2;
const char *error; const char *error;
int ret; int ret;
temp_set.close_on_free = FALSE;
temp_set.unlink_on_free = FALSE;
file_unlock_real(lock); file_unlock_real(lock);
ret = file_try_lock_error(lock->fd, lock->path, F_WRLCK, ret = file_try_lock(lock->fd, lock->path, F_WRLCK, &temp_set,
lock->lock_method, &temp_lock, &error); &temp_lock, &error);
if (ret < 0) { if (ret < 0) {
i_error("file_lock_free(): Unexpectedly failed to retry locking % s: %s", i_error("file_lock_free(): Unexpectedly failed to retry locking % s: %s",
lock->path, error); lock->path, error);
} else if (ret == 0) { } else if (ret == 0) {
/* already locked by someone else */ /* already locked by someone else */
} else if (fstat(lock->fd, &st1) < 0) { } else if (fstat(lock->fd, &st1) < 0) {
/* not expected to happen */ /* not expected to happen */
i_error("file_lock_free(): fstat(%s) failed: %m", lock->path); i_error("file_lock_free(): fstat(%s) failed: %m", lock->path);
} else if (stat(lock->path, &st2) < 0) { } else if (stat(lock->path, &st2) < 0) {
if (errno != ENOENT) if (errno != ENOENT)
skipping to change at line 441 skipping to change at line 429
{ {
struct file_lock *lock = *_lock; struct file_lock *lock = *_lock;
if (lock == NULL) if (lock == NULL)
return; return;
*_lock = NULL; *_lock = NULL;
if (lock->dotlock != NULL) if (lock->dotlock != NULL)
file_dotlock_delete(&lock->dotlock); file_dotlock_delete(&lock->dotlock);
if (lock->unlink_on_free) if (lock->set.unlink_on_free)
file_try_unlink_locked(lock); file_try_unlink_locked(lock);
if (lock->close_on_free) if (lock->set.close_on_free)
i_close_fd(&lock->fd); i_close_fd(&lock->fd);
file_lock_log_warning_if_slow(lock); file_lock_log_warning_if_slow(lock);
i_free(lock->path); i_free(lock->path);
i_free(lock); i_free(lock);
} }
const char *file_lock_get_path(struct file_lock *lock) const char *file_lock_get_path(struct file_lock *lock)
{ {
return lock->path; return lock->path;
 End of changes. 27 change blocks. 
55 lines changed or deleted 41 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)