Mercurial > dovecot > original-hg > dovecot-1.2
changeset 643:da34bdd4e0c6 HEAD
Added mbox lock settings to config file. Support timeouting fcntl() and
flock() locks. Plus before the fcntl/flocks weren't even set.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 21 Nov 2002 22:13:32 +0200 |
parents | 5f5fdc45c19d |
children | 415498fa78d6 |
files | dovecot-example.conf src/lib-index/mail-index.h src/lib-index/mbox/mbox-lock.c src/master/imap-process.c src/master/settings.c src/master/settings.h |
diffstat | 6 files changed, 244 insertions(+), 61 deletions(-) [+] |
line wrap: on
line diff
--- a/dovecot-example.conf Thu Nov 21 16:56:07 2002 +0200 +++ b/dovecot-example.conf Thu Nov 21 22:13:32 2002 +0200 @@ -177,6 +177,26 @@ # down things as extra stat() needs to be called for each file. #maildir_check_content_changes = no +# Which locking methods to use for locking mbox. All of them are used by +# default. flock is ignored in systems which don't have it. Note that the +# order of fcntl and flock are important to prevent deadlocks if they're both +# also used by other programs accessing the mailbox. Dotlock file is always +# created first. +#mbox_locks = dotlock fcntl flock + +# Should we create dotlock file even when we want only a read-lock? Setting +# this to yes hurts the performance when the mailbox is accessed simultaneously +# by multiple processes, but it's needed for reliable reading if no other +# locking methods are available. +#mbox_read_dotlock = no + +# Maximum time in seconds to wait for lock (all of them) before aborting. +#mbox_lock_timeout = 300 + +# If dotlock exists but the mailbox isn't modified in any way, override the +# lock file after this many seconds. +#mbox_dotlock_change_timeout = 30 + # If main index file is incompatible with us, should we overwrite it or # create a new index with another name. Unless you are running Dovecot in # multiple computers with different architectures accessing the same
--- a/src/lib-index/mail-index.h Thu Nov 21 16:56:07 2002 +0200 +++ b/src/lib-index/mail-index.h Thu Nov 21 22:13:32 2002 +0200 @@ -333,6 +333,8 @@ int mbox_fd; IBuffer *mbox_inbuf; MailLockType mbox_lock_type; + dev_t mbox_dotlock_dev; + ino_t mbox_dotlock_ino; /* these counters can be used to check that we've synced the mailbox after locking it */ @@ -374,7 +376,7 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \ - 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0 /* defaults - same as above but prefixed with mail_index_. */ int mail_index_open(MailIndex *index, int update_recent, int fast);
--- a/src/lib-index/mbox/mbox-lock.c Thu Nov 21 16:56:07 2002 +0200 +++ b/src/lib-index/mbox/mbox-lock.c Thu Nov 21 22:13:32 2002 +0200 @@ -18,17 +18,53 @@ /* 0.1 .. 0.2msec */ #define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000) -/* assume stale dotlock if mbox file hasn't changed for 5 seconds */ -#define MAX_UNCHANGED_LOCK_WAIT 5 +/* lock methods to use in wanted order */ +#define DEFAULT_LOCK_METHODS "dotlock fcntl flock" +/* lock timeout */ +#define DEFAULT_LOCK_TIMEOUT 300 +/* assume stale dotlock if mbox file hasn't changed for n seconds */ +#define DEFAULT_DOTLOCK_CHANGE_TIMEOUT 30 + +static int lock_settings_initialized = FALSE; +static int use_dotlock, use_fcntl_lock, use_flock, fcntl_before_flock; +static int use_read_dotlock, lock_timeout, dotlock_change_timeout; + +static void mbox_init_lock_settings(void) +{ + const char *str; + char *const *lock; + + use_dotlock = use_fcntl_lock = use_flock = fcntl_before_flock = FALSE; -/* abort trying to get lock after 30 seconds */ -#define MAX_LOCK_WAIT 30 + str = getenv("MBOX_LOCKS"); + if (str == NULL) str = DEFAULT_LOCK_METHODS; + for (lock = t_strsplit(str, " "); *lock != NULL; lock++) { + if (strcasecmp(*lock, "dotlock") == 0) + use_dotlock = TRUE; + else if (strcasecmp(*lock, "fcntl") == 0) { + use_fcntl_lock = TRUE; + fcntl_before_flock = use_flock == FALSE; + } else if (strcasecmp(*lock, "flock") == 0) + use_flock = TRUE; + else + i_fatal("MBOX_LOCKS: Invalid value %s", *lock); + } -/* remove lock after 10 mins */ -#define STALE_LOCK_TIMEOUT (60*10) + use_read_dotlock = getenv("MBOX_READ_DOTLOCK") != NULL; + + str = getenv("MBOX_LOCK_TIMEOUT"); + lock_timeout = str == NULL ? DEFAULT_LOCK_TIMEOUT : atoi(str); + + str = getenv("MBOX_DOTLOCK_CHANGE_TIMEOUT"); + dotlock_change_timeout = str == NULL ? + DEFAULT_DOTLOCK_CHANGE_TIMEOUT : atoi(str); + + lock_settings_initialized = TRUE; +} #ifdef HAVE_FLOCK -static int mbox_lock_flock(MailIndex *index, MailLockType lock_type) +static int mbox_lock_flock(MailIndex *index, MailLockType lock_type, + time_t max_wait_time) { if (lock_type == MAIL_LOCK_EXCLUSIVE) lock_type = LOCK_EX; @@ -37,15 +73,27 @@ else lock_type = LOCK_UN; - if (flock(index->mbox_fd, lock_type) < 0) - return index_file_set_syscall_error(index, index->mbox_path, - "flock()"); + while (flock(index->mbox_fd, lock_type | LOCK_NB) < 0) { + if (errno != EWOULDBLOCK) { + index_file_set_syscall_error(index, index->mbox_path, + "flock()"); + return FALSE; + } + + if (max_wait_time != 0 && time(NULL) >= max_wait_time) { + errno = EAGAIN; + return FALSE; + } + + usleep(LOCK_RANDOM_USLEEP_TIME); + } return TRUE; } #endif -static int mbox_lock_fcntl(MailIndex *index, MailLockType lock_type) +static int mbox_lock_fcntl(MailIndex *index, MailLockType lock_type, + time_t max_wait_time) { struct flock fl; @@ -60,45 +108,34 @@ "fcntl()"); return FALSE; } + + if (max_wait_time != 0 && time(NULL) >= max_wait_time) { + errno = EAGAIN; + return FALSE; + } } return TRUE; } -static int mbox_lock_dotlock(MailIndex *index, const char *path, int set) +static int mbox_lock_dotlock(MailIndex *index, const char *path, + time_t max_wait_time) { struct stat st; - time_t now, max_wait_time, last_change, last_mtime; + time_t now, last_change, last_mtime; off_t last_size; int fd; path = t_strconcat(path, ".lock", NULL); - if (!set) { - if (unlink(path) == 0 || errno == ENOENT) - return TRUE; - - return index_file_set_syscall_error(index, path, "unlink()"); - } /* don't bother with the temp files as we'd just leave them lying around. besides, postfix also relies on O_EXCL working so we might as well. */ - max_wait_time = time(NULL) + MAX_LOCK_WAIT; last_change = time(NULL); last_size = 0; last_mtime = 0; do { now = time(NULL); if (stat(path, &st) == 0) { - /* lock exists, see if it's too old */ - if (now > st.st_ctime + STALE_LOCK_TIMEOUT) { - if (unlink(path) < 0 && errno != ENOENT) { - index_file_set_syscall_error( - index, path, "unlink()"); - break; - } - continue; - } - /* see if there's been any changes in mbox */ if (stat(index->mbox_path, &st) < 0) { mbox_set_syscall_error(index, "stat()"); @@ -112,7 +149,7 @@ last_mtime = st.st_mtime; } - if (now > last_change + MAX_UNCHANGED_LOCK_WAIT) { + if (now > last_change + dotlock_change_timeout) { /* no changes for a while, assume stale lock */ if (unlink(path) < 0 && errno != ENOENT) { index_file_set_syscall_error( @@ -129,9 +166,20 @@ fd = open(path, O_WRONLY | O_EXCL | O_CREAT, 0); if (fd != -1) { /* got it */ + if (fstat(fd, &st) < 0) { + index_file_set_syscall_error(index, path, + "fstat()"); + (void)close(fd); + return FALSE; + } + + index->mbox_dotlock_dev = st.st_dev; + index->mbox_dotlock_ino = st.st_ino; + if (close(fd) < 0) { index_file_set_syscall_error(index, path, "close()"); + return FALSE; } return TRUE; } @@ -147,9 +195,67 @@ return FALSE; } +static int mbox_unlock_dotlock(MailIndex *index, const char *path) +{ + struct stat st; + dev_t old_dev; + ino_t old_ino; + + path = t_strconcat(path, ".lock", NULL); + + old_dev = index->mbox_dotlock_dev; + old_ino = index->mbox_dotlock_ino; + + index->mbox_dotlock_dev = 0; + index->mbox_dotlock_ino = 0; + + if (stat(path, &st) < 0) { + if (errno == ENOENT) + return TRUE; /* doesn't exist anymore, ignore */ + + index_file_set_syscall_error(index, path, "stat()"); + return FALSE; + } + + /* make sure it's still our dotlock */ + if (old_dev != st.st_dev || old_ino != st.st_ino) { + index_set_error(index, + "Warning: Our dotlock file %s was overridden", path); + return FALSE; + } + + if (unlink(path) < 0 && errno != ENOENT) + return index_file_set_syscall_error(index, path, "unlink()"); + + return TRUE; +} + +static int mbox_file_locks(MailIndex *index, time_t max_wait_time) +{ + if (use_fcntl_lock && fcntl_before_flock) { + if (!mbox_lock_fcntl(index, index->mbox_lock_type, + max_wait_time)) + return FALSE; + } +#ifdef HAVE_FLOCK + if (use_flock) { + if (!mbox_lock_flock(index, index->mbox_lock_type, + max_wait_time)) + return FALSE; + } +#endif + if (use_fcntl_lock && !fcntl_before_flock) { + if (!mbox_lock_fcntl(index, index->mbox_lock_type, + max_wait_time)) + return FALSE; + } + return TRUE; +} + int mbox_lock(MailIndex *index, MailLockType lock_type) { struct stat st; + time_t max_wait_time; /* index must be locked before mbox file, to avoid deadlocks */ i_assert(index->lock_type != MAIL_LOCK_UNLOCK); @@ -163,40 +269,39 @@ if (index->mbox_lock_type == lock_type) return TRUE; + if (!lock_settings_initialized) + mbox_init_lock_settings(); + + max_wait_time = time(NULL) + lock_timeout; + /* make .lock file first to protect overwriting the file */ - if (index->mbox_lock_type == MAIL_LOCK_UNLOCK) { - if (!mbox_lock_dotlock(index, index->mbox_path, TRUE)) + if (use_dotlock && index->mbox_dotlock_ino == 0 && + (lock_type == MAIL_LOCK_EXCLUSIVE || use_read_dotlock)) { + if (!mbox_lock_dotlock(index, index->mbox_path, max_wait_time)) return FALSE; } /* now we need to have the file itself locked. open it if needed. */ - do { - if (stat(index->mbox_path, &st) < 0) - return mbox_set_syscall_error(index, "stat()"); + if (stat(index->mbox_path, &st) < 0) + return mbox_set_syscall_error(index, "stat()"); - if (st.st_dev != index->mbox_dev || - st.st_ino != index->mbox_ino) - mbox_file_close_fd(index); - - if (index->mbox_fd == -1) { - if (!mbox_file_open(index)) - break; - } + if (st.st_dev != index->mbox_dev || st.st_ino != index->mbox_ino) + mbox_file_close_fd(index); - if (!mbox_lock_fcntl(index, index->mbox_lock_type)) - break; -#ifdef HAVE_FLOCK - if (!mbox_lock_flock(index, index->mbox_lock_type)) - break; -#endif - index->mbox_lock_type = lock_type; - return TRUE; - } while (0); + if (index->mbox_fd == -1) { + if (!mbox_file_open(index)) { + (void)mbox_unlock(index); + return FALSE; + } + } - if (index->mbox_lock_type == MAIL_LOCK_UNLOCK) - (void)mbox_lock_dotlock(index, index->mbox_path, FALSE); + index->mbox_lock_type = lock_type; + if (!mbox_file_locks(index, max_wait_time)) { + (void)mbox_unlock(index); + return FALSE; + } - return FALSE; + return TRUE; } int mbox_unlock(MailIndex *index) @@ -211,15 +316,18 @@ failed = FALSE; if (index->mbox_fd != -1) { #ifdef HAVE_FLOCK - if (!mbox_lock_flock(index, MAIL_LOCK_UNLOCK)) + if (use_flock && !mbox_lock_flock(index, MAIL_LOCK_UNLOCK, 0)) failed = TRUE; #endif - if (!mbox_lock_fcntl(index, MAIL_LOCK_UNLOCK)) + if (use_fcntl_lock && + !mbox_lock_fcntl(index, MAIL_LOCK_UNLOCK, 0)) failed = TRUE; } - if (!mbox_lock_dotlock(index, index->mbox_path, FALSE)) - failed = TRUE; + if (index->mbox_dotlock_ino != 0) { + if (!mbox_unlock_dotlock(index, index->mbox_path)) + failed = TRUE; + } /* make sure we don't keep mmap() between locks - there could have been changes to file size which would break things. or actually
--- a/src/master/imap-process.c Thu Nov 21 16:56:07 2002 +0200 +++ b/src/master/imap-process.c Thu Nov 21 22:13:32 2002 +0200 @@ -133,6 +133,14 @@ if (umask(set_umask) != set_umask) i_fatal("Invalid umask: %o", set_umask); + putenv((char *) t_strconcat("MBOX_LOCKS=", set_mbox_locks, NULL)); + putenv((char *) t_strdup_printf("MBOX_LOCK_TIMEOUT=%u", + set_mbox_lock_timeout)); + putenv((char *) t_strdup_printf("MBOX_DOTLOCK_CHANGE_TIMEOUT=%u", + set_mbox_dotlock_change_timeout)); + if (set_mbox_read_dotlock) + putenv("MBOX_READ_DOTLOCK=1"); + if (set_verbose_proctitle && net_ip2host(ip, host) == 0) { i_snprintf(title, sizeof(title), "[%s %s]", user, host); argv[2] = title;
--- a/src/master/settings.c Thu Nov 21 16:56:07 2002 +0200 +++ b/src/master/settings.c Thu Nov 21 22:13:32 2002 +0200 @@ -66,6 +66,11 @@ SET_BOOL,&set_maildir_copy_with_hardlinks }, { "maildir_check_content_changes", SET_BOOL,&set_maildir_check_content_changes }, + { "mbox_locks", SET_STR, &set_mbox_locks, }, + { "mbox_read_dotlock", SET_BOOL,&set_mbox_read_dotlock, }, + { "mbox_lock_timeout", SET_INT, &set_mbox_lock_timeout, }, + { "mbox_dotlock_change_timeout", + SET_INT, &set_mbox_dotlock_change_timeout, }, { "overwrite_incompatible_index", SET_BOOL,&set_overwrite_incompatible_index }, { "umask", SET_INT, &set_umask }, @@ -118,6 +123,10 @@ int set_mail_save_crlf = FALSE; int set_maildir_copy_with_hardlinks = FALSE; int set_maildir_check_content_changes = FALSE; +char *set_mbox_locks = "dotlock fcntl flock"; +int set_mbox_read_dotlock = FALSE; +unsigned int set_mbox_lock_timeout = 300; +unsigned int set_mbox_dotlock_change_timeout = 30; int set_overwrite_incompatible_index = FALSE; unsigned int set_umask = 0077; @@ -154,6 +163,9 @@ static void settings_verify(void) { + char *const *str; + int dotlock_got, fcntl_got, flock_got; + get_login_uid(); if (access(set_login_executable, X_OK) < 0) { @@ -188,6 +200,35 @@ set_first_valid_gid > set_last_valid_gid) i_fatal("first_valid_gid can't be larger than last_valid_gid"); + dotlock_got = fcntl_got = flock_got = FALSE; + for (str = t_strsplit(set_mbox_locks, " "); *str != NULL; str++) { + if (strcasecmp(*str, "dotlock") == 0) + dotlock_got = TRUE; + else if (strcasecmp(*str, "fcntl") == 0) + fcntl_got = TRUE; + else if (strcasecmp(*str, "flock") == 0) + flock_got = TRUE; + else + i_fatal("mbox_locks: Invalid value %s", *str); + } + +#ifndef HAVE_FLOCK + if (fcntl_got && !dotlock_got && !flock_got) { + i_fatal("mbox_locks: Only flock selected, " + "and flock() isn't supported in this system"); + } + flock_got = FALSE; +#endif + + if (!dotlock_got && !fcntl_got && !flock_got) + i_fatal("mbox_locks: No mbox locking methods selected"); + + if (dotlock_got && !set_mbox_read_dotlock && !fcntl_got && !flock_got) { + i_warning("mbox_locks: Only dotlock selected, forcing " + "mbox_read_dotlock = yes to avoid corruption."); + set_mbox_read_dotlock = TRUE; + } + auth_settings_verify(); }
--- a/src/master/settings.h Thu Nov 21 16:56:07 2002 +0200 +++ b/src/master/settings.h Thu Nov 21 22:13:32 2002 +0200 @@ -45,6 +45,10 @@ extern int set_mail_save_crlf; extern int set_maildir_copy_with_hardlinks; extern int set_maildir_check_content_changes; +extern char *set_mbox_locks; +extern int set_mbox_read_dotlock; +extern unsigned int set_mbox_lock_timeout; +extern unsigned int set_mbox_dotlock_change_timeout; extern int set_overwrite_incompatible_index; extern unsigned int set_umask;