[dovecot-cvs] dovecot/src/lib-index/mbox mbox-lock.c,1.12,1.13
cras at procontrol.fi
cras at procontrol.fi
Thu Nov 21 22:13:34 EET 2002
Update of /home/cvs/dovecot/src/lib-index/mbox
In directory danu:/tmp/cvs-serv16852/src/lib-index/mbox
Modified Files:
mbox-lock.c
Log Message:
Added mbox lock settings to config file. Support timeouting fcntl() and
flock() locks. Plus before the fcntl/flocks weren't even set.
Index: mbox-lock.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-index/mbox/mbox-lock.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -d -r1.12 -r1.13
--- mbox-lock.c 4 Nov 2002 06:20:35 -0000 1.12
+++ mbox-lock.c 21 Nov 2002 20:13:32 -0000 1.13
@@ -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
-/* abort trying to get lock after 30 seconds */
-#define MAX_LOCK_WAIT 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;
-/* remove lock after 10 mins */
-#define STALE_LOCK_TIMEOUT (60*10)
+static void mbox_init_lock_settings(void)
+{
+ const char *str;
+ char *const *lock;
+
+ use_dotlock = use_fcntl_lock = use_flock = fcntl_before_flock = FALSE;
+
+ 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);
+ }
+
+ 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 (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 (index->mbox_fd == -1) {
+ if (!mbox_file_open(index)) {
+ (void)mbox_unlock(index);
+ return FALSE;
}
+ }
- 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_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
More information about the dovecot-cvs
mailing list