changeset 674:b7aefd0d7611 HEAD

Locking changes triggered a bit larger cleanup :) If we have to wait for a lock longer, the client is now notified about it every 30 seconds. Also if mailbox opening fails because of lock timeout, we won't overwrite the index anymore. Finally user gets a clear error message about lock timeout instead of "internal error".
author Timo Sirainen <tss@iki.fi>
date Mon, 25 Nov 2002 21:02:49 +0200
parents d864b45e708e
children a26d28bbc3ff
files TODO src/imap/Makefile.am src/imap/client.c src/imap/cmd-select.c src/imap/mail-storage-callbacks.c src/imap/mailbox-sync.c src/lib-index/mail-custom-flags.c src/lib-index/mail-index-open.c src/lib-index/mail-index-util.c src/lib-index/mail-index-util.h src/lib-index/mail-index.c src/lib-index/mail-index.h src/lib-index/mail-modifylog.c src/lib-index/maildir/maildir-index.c src/lib-index/mbox/mbox-index.c src/lib-index/mbox/mbox-lock.c src/lib-storage/index/index-copy.c src/lib-storage/index/index-expunge.c src/lib-storage/index/index-fetch.c src/lib-storage/index/index-messageset.c src/lib-storage/index/index-search.c src/lib-storage/index/index-status.c src/lib-storage/index/index-storage.c src/lib-storage/index/index-storage.h src/lib-storage/index/index-sync.c src/lib-storage/index/index-update-flags.c src/lib-storage/index/maildir/maildir-copy.c src/lib-storage/index/maildir/maildir-storage.c src/lib-storage/index/mbox/mbox-save.c src/lib-storage/index/mbox/mbox-storage.c src/lib-storage/mail-storage.h src/lib-storage/subscription-file/subscription-file.c src/lib/file-lock.c src/lib/file-lock.h
diffstat 34 files changed, 631 insertions(+), 356 deletions(-) [+]
line wrap: on
line diff
--- a/TODO	Mon Nov 25 17:00:53 2002 +0200
+++ b/TODO	Mon Nov 25 21:02:49 2002 +0200
@@ -2,6 +2,8 @@
     - maildir: if mail file isn't found, it may be because it was renamed
       (flag changed). we must then sync the directory and see again if the mail
       is found
+    - we may override mbox dotlock file, but then get stuck at fcntl/flock.
+      we should check for those before overriding the mbox lock..
     - mail-lockdir.c isn't 100% safe.. stale locks are detected by checking
       that hard link count is 1, then it's unlink()ed. but what if another
       process did the same unlink() + creat() in the middle of our
@@ -21,10 +23,7 @@
       the old file.. and check that deleting file does "inconsistency error"
     - if imap process notices that both modify logs are getting full because
       it's client isn't syncing, the client should be disconnected
-    - if opening indexes fails because we timeout while trying to lock it, we
-      recreate the indexes. that's not very good idea .. also does it do that
-      even if .customflags can't be locked? it's not really related to
-      indexes..
+    - what happens if .customflags can't be locked while opening index?
 
  - checks:
    - if we have entries in modifylog with UID 10..11, 9..12, 8..13 etc.
@@ -36,13 +35,7 @@
    - make sure connection limits work
 
  - enhancements:
-    - "* NO Stale mailbox lock file detected, will override in xx seconds",
-      "* NO Mailbox is locked, will abort in xx seconds",
-      "* NO Mailbox index is locked, will abort in xx seconds"
-        - before even considering this, check that we can do fcntl() locking
-	- don't give internal error to user but "mailbox is locked" or
-	  "index is locked" when we can't do something because of locking
-	- optionally don't fail if index is locked, but build it in memory
+    - optionally don't fail if index is locked, but build it in memory
     - when fetching body/envelope/etc we could try to cache it immediately if
       we can get lock with try_lock.
     - optionally use only in-memory indexes
--- a/src/imap/Makefile.am	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/imap/Makefile.am	Mon Nov 25 21:02:49 2002 +0200
@@ -53,7 +53,7 @@
 	client.c \
 	commands.c \
 	commands-util.c \
-	mailbox-sync.c \
+	mail-storage-callbacks.c \
 	main.c \
 	rawlog.c
 
--- a/src/imap/client.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/imap/client.c	Mon Nov 25 21:02:49 2002 +0200
@@ -25,6 +25,8 @@
 /* Disconnect client after idling this many seconds */
 #define CLIENT_IDLE_TIMEOUT (60*30)
 
+extern MailStorageCallbacks mail_storage_callbacks;
+
 static Client *my_client; /* we don't need more than one currently */
 static Timeout to_idle;
 
@@ -76,6 +78,7 @@
         client->last_input = ioloop_time;
 
 	client->storage = storage;
+	storage->set_callbacks(storage, &mail_storage_callbacks, client);
 
 	i_assert(my_client == NULL);
 	my_client = client;
--- a/src/imap/cmd-select.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/imap/cmd-select.c	Mon Nov 25 21:02:49 2002 +0200
@@ -4,8 +4,6 @@
 #include "temp-string.h"
 #include "commands.h"
 
-extern MailboxSyncCallbacks sync_callbacks;
-
 int cmd_select_full(Client *client, int readonly)
 {
 	Mailbox *box;
@@ -16,28 +14,29 @@
 	if (!client_read_string_args(client, 1, &mailbox))
 		return FALSE;
 
-	if (client->mailbox != NULL)
+	if (client->mailbox != NULL) {
 		client->mailbox->close(client->mailbox);
+		client->mailbox = NULL;
+	}
 
-	client->mailbox = client->storage->open_mailbox(client->storage,
-							mailbox, readonly,
-							FALSE);
-	if (client->mailbox == NULL) {
+	box = client->storage->open_mailbox(client->storage, mailbox,
+					    readonly, FALSE);
+	if (box == NULL) {
 		client_send_storage_error(client);
 		return TRUE;
 	}
 
-	box = client->mailbox;
 	if (!box->get_status(box, STATUS_MESSAGES | STATUS_RECENT |
 			     STATUS_FIRST_UNSEEN_SEQ | STATUS_UIDVALIDITY |
 			     STATUS_CUSTOM_FLAGS, &status)) {
 		client_send_storage_error(client);
+		box->close(box);
 		return TRUE;
 	}
 
-	/* set callbacks after STATUS, which might otherwise try calling
-	   some of them */
-	box->set_sync_callbacks(box, &sync_callbacks, client);
+	/* set client's mailbox only after getting status to make sure
+	   we're not sending any expunge/exists replies too early to client */
+	client->mailbox = box;
 
 	client_send_mailbox_flags(client, box, status.custom_flags,
 				  status.custom_flags_count);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap/mail-storage-callbacks.c	Mon Nov 25 21:02:49 2002 +0200
@@ -0,0 +1,106 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "common.h"
+#include "obuffer.h"
+#include "imap-util.h"
+#include "commands-util.h"
+
+static void alert_no_diskspace(Mailbox *mailbox __attr_unused__, void *context)
+{
+	Client *client = context;
+
+	client_send_line(client, "* NO [ALERT] "
+			 "Disk space is full, delete some messages.");
+}
+
+static void notify_ok(Mailbox *mailbox __attr_unused__,
+		      const char *text, void *context)
+{
+	Client *client = context;
+
+	client_send_line(client, t_strconcat("* OK ", text, NULL));
+	o_buffer_flush(client->outbuf);
+}
+
+static void notify_no(Mailbox *mailbox __attr_unused__,
+		      const char *text, void *context)
+{
+	Client *client = context;
+
+	client_send_line(client, t_strconcat("* NO ", text, NULL));
+	o_buffer_flush(client->outbuf);
+}
+
+static void expunge(Mailbox *mailbox, unsigned int seq, void *context)
+{
+	Client *client = context;
+	char str[MAX_LARGEST_T_STRLEN+20];
+
+	if (client->mailbox != mailbox)
+		return;
+
+	i_snprintf(str, sizeof(str), "* %u EXPUNGE", seq);
+	client_send_line(client, str);
+}
+
+static void update_flags(Mailbox *mailbox, unsigned int seq, unsigned int uid,
+			 MailFlags flags, const char *custom_flags[],
+			 unsigned int custom_flags_count, void *context)
+{
+	Client *client = context;
+	const char *str;
+
+	if (client->mailbox != mailbox)
+		return;
+
+	t_push();
+	str = imap_write_flags(flags, custom_flags, custom_flags_count);
+
+	if (client->sync_flags_send_uid) {
+		str = t_strdup_printf("* %u FETCH (FLAGS (%s) UID %u)",
+				      seq, str, uid);
+	} else {
+		str = t_strdup_printf("* %u FETCH (FLAGS (%s))", seq, str);
+	}
+
+	client_send_line(client, str);
+	t_pop();
+}
+
+static void new_messages(Mailbox *mailbox, unsigned int messages_count,
+			 unsigned int recent_count, void *context)
+{
+	Client *client = context;
+	char str[MAX_LARGEST_T_STRLEN+20];
+
+	if (client->mailbox != mailbox)
+		return;
+
+	i_snprintf(str, sizeof(str), "* %u EXISTS", messages_count);
+	client_send_line(client, str);
+
+	i_snprintf(str, sizeof(str), "* %u RECENT", recent_count);
+	client_send_line(client, str);
+}
+
+static void new_custom_flags(Mailbox *mailbox, const char *custom_flags[],
+			     unsigned int custom_flags_count, void *context)
+{
+	Client *client = context;
+
+	if (client->mailbox != mailbox)
+		return;
+
+	client_send_mailbox_flags(client, mailbox, custom_flags,
+				  custom_flags_count);
+}
+
+MailStorageCallbacks mail_storage_callbacks = {
+	alert_no_diskspace,
+	notify_ok,
+	notify_no,
+	expunge,
+	update_flags,
+	new_messages,
+	new_custom_flags
+};
--- a/src/imap/mailbox-sync.c	Mon Nov 25 17:00:53 2002 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "common.h"
-#include "imap-util.h"
-#include "commands-util.h"
-
-static void sync_alert_no_diskspace(Mailbox *mailbox __attr_unused__,
-				    void *context)
-{
-	Client *client = context;
-
-	client_send_line(client, "* NO [ALERT] "
-			 "Disk space is full, delete some messages.");
-
-}
-
-static void sync_expunge(Mailbox *mailbox __attr_unused__, unsigned int seq,
-			 void *context)
-{
-	Client *client = context;
-	char str[MAX_LARGEST_T_STRLEN+20];
-
-	i_snprintf(str, sizeof(str), "* %u EXPUNGE", seq);
-	client_send_line(client, str);
-}
-
-static void sync_update_flags(Mailbox *mailbox __attr_unused__,
-			      unsigned int seq, unsigned int uid,
-			      MailFlags flags, const char *custom_flags[],
-			      unsigned int custom_flags_count, void *context)
-{
-	Client *client = context;
-	const char *str;
-
-	t_push();
-	str = imap_write_flags(flags, custom_flags, custom_flags_count);
-
-	if (client->sync_flags_send_uid) {
-		str = t_strdup_printf("* %u FETCH (FLAGS (%s) UID %u)",
-				      seq, str, uid);
-	} else {
-		str = t_strdup_printf("* %u FETCH (FLAGS (%s))", seq, str);
-	}
-
-	client_send_line(client, str);
-	t_pop();
-}
-
-static void sync_new_messages(Mailbox *mailbox __attr_unused__,
-			      unsigned int messages_count,
-			      unsigned int recent_count, void *context)
-{
-	Client *client = context;
-	char str[MAX_LARGEST_T_STRLEN+20];
-
-	i_snprintf(str, sizeof(str), "* %u EXISTS", messages_count);
-	client_send_line(client, str);
-
-	i_snprintf(str, sizeof(str), "* %u RECENT", recent_count);
-	client_send_line(client, str);
-}
-
-static void sync_new_custom_flags(Mailbox *mailbox, const char *custom_flags[],
-				  unsigned int custom_flags_count,
-				  void *context)
-{
-	Client *client = context;
-
-	client_send_mailbox_flags(client, mailbox, custom_flags,
-				  custom_flags_count);
-}
-
-MailboxSyncCallbacks sync_callbacks = {
-	sync_alert_no_diskspace,
-	sync_expunge,
-	sync_update_flags,
-	sync_new_messages,
-	sync_new_custom_flags
-};
--- a/src/lib-index/mail-custom-flags.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-index/mail-custom-flags.c	Mon Nov 25 21:02:49 2002 +0200
@@ -195,7 +195,8 @@
 	if (mcf->lock_type == type)
 		return TRUE;
 
-	if (file_wait_lock(mcf->fd, type, DEFAULT_LOCK_TIMEOUT) <= 0)
+	/* FIXME: possibility to use .lock file instead */
+	if (file_wait_lock(mcf->fd, type) <= 0)
 		return index_cf_set_syscall_error(mcf, "file_wait_lock()");
 
 	mcf->lock_type = type;
--- a/src/lib-index/mail-index-open.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-index/mail-index-open.c	Mon Nov 25 21:02:49 2002 +0200
@@ -245,49 +245,53 @@
 	return TRUE;
 }
 
+static int mail_index_verify_header(MailIndex *index, MailIndexHeader *hdr)
+{
+	/* if index is being created, we'll wait here until it's finished */
+	if (!mail_index_wait_lock(index, F_RDLCK))
+		return FALSE;
+
+	/* check the compatibility anyway just to be sure */
+	if (!read_and_verify_header(index->fd, hdr, TRUE)) {
+		index_set_error(index, "Non-compatible index file %s",
+				index->filepath);
+		(void)mail_index_wait_lock(index, F_UNLCK);
+		return FALSE;
+	}
+
+	if (!mail_index_wait_lock(index, F_UNLCK))
+		return FALSE;
+
+	return TRUE;
+}
+
 static int mail_index_open_file(MailIndex *index, const char *path,
 				int update_recent, int fast)
 {
         MailIndexHeader hdr;
-	int fd, failed;
 
 	/* the index file should already be checked that it exists and
 	   we're compatible with it. */
 
-	fd = open(path, O_RDWR);
-	if (fd == -1)
+	index->fd = open(path, O_RDWR);
+	if (index->fd == -1)
 		return index_file_set_syscall_error(index, path, "open()");
+	index->filepath = i_strdup(path);
 
-	/* if index is being created, we'll wait here until it's finished */
-	if (file_wait_lock(fd, F_RDLCK, DEFAULT_LOCK_TIMEOUT) <= 0) {
-		index_file_set_syscall_error(index, path, "file_wait_lock()");
-		(void)close(fd);
+	if (!mail_index_verify_header(index, &hdr)) {
+		(void)close(index->fd);
+		index->fd = -1;
+
+		i_free(index->filepath);
+		index->filepath = NULL;
 		return FALSE;
 	}
 
-	/* check the compatibility anyway just to be sure */
-	if (!read_and_verify_header(fd, &hdr, TRUE)) {
-		index_set_error(index, "Non-compatible index file %s", path);
-		(void)close(fd);
-		return FALSE;
-	}
-
-	if (file_wait_lock(fd, F_UNLCK, 0) <= 0) {
-		index_file_set_syscall_error(index, path, "file_wait_lock()");
-		(void)close(fd);
-		return FALSE;
-	}
-
-	index->fd = fd;
-	index->filepath = i_strdup(path);
 	index->indexid = hdr.indexid;
 
-	failed = !index_open_and_fix(index, update_recent, fast);
-
-	if (!index->set_lock(index, MAIL_LOCK_UNLOCK))
-		failed = TRUE;
-
-	if (failed) {
+	/* the shared lock set is just to make sure we drop exclusive lock */
+	if (!index_open_and_fix(index, update_recent, fast) ||
+	    !index->set_lock(index, MAIL_LOCK_UNLOCK)) {
 		mail_index_close(index);
 		return FALSE;
 	}
@@ -296,43 +300,48 @@
 }
 
 static int mail_index_init_new_file(MailIndex *index, MailIndexHeader *hdr,
-				    int fd, const char *path,
-				    const char **index_path)
+				    const char *temp_path)
 {
-	off_t fsize;
+	const char *index_path;
+	uoff_t fsize;
 
-	*index_path = NULL;
+	/* set the index's path temporarily */
+	index->filepath = t_strdup_noconst(temp_path);
 
-	if (write_full(fd, hdr, sizeof(MailIndexHeader)) < 0) {
-		index_file_set_syscall_error(index, path, "write_full()");
+	if (write_full(index->fd, hdr, sizeof(MailIndexHeader)) < 0) {
+		index_set_syscall_error(index, "write_full()");
+		index->filepath = NULL;
 		return FALSE;
 	}
 
 	fsize = sizeof(MailIndexHeader) +
 		INDEX_MIN_RECORDS_COUNT * sizeof(MailIndexRecord);
-	if (file_set_size(fd, (off_t)fsize) < 0) {
-		index_file_set_syscall_error(index, path, "file_set_size()");
+	if (file_set_size(index->fd, fsize) < 0) {
+		index_set_syscall_error(index, "file_set_size()");
+		index->filepath = NULL;
 		return FALSE;
 	}
 
-	if (file_wait_lock(fd, F_WRLCK, DEFAULT_LOCK_TIMEOUT) <= 0) {
-		index_file_set_syscall_error(index, path, "file_wait_lock()");
+	if (mail_index_wait_lock(index, F_WRLCK) <= 0) {
+		index->filepath = NULL;
 		return FALSE;
 	}
+	index->filepath = NULL;
 
 	/* move the temp index into the real one. we also need to figure
 	   out what to call ourself on the way. */
-	*index_path = t_strconcat(index->dir, "/"INDEX_FILE_PREFIX, NULL);
-	if (link(path, *index_path) == 0) {
-		if (unlink(path) < 0) {
+	index_path = t_strconcat(index->dir, "/"INDEX_FILE_PREFIX, NULL);
+	if (link(temp_path, index_path) == 0) {
+		if (unlink(temp_path) < 0) {
 			/* doesn't really matter, log anyway */
-			index_file_set_syscall_error(index, path, "unlink()");
+			index_file_set_syscall_error(index, temp_path,
+						     "unlink()");
 		}
 	} else {
 		if (errno != EEXIST) {
 			/* fatal error */
 			index_set_error(index, "link(%s, %s) failed: %m",
-					path, *index_path);
+					temp_path, index_path);
 			return FALSE;
 		}
 
@@ -345,17 +354,18 @@
 			   override previous index as well */
 			hostpid_init();
 
-			*index_path = t_strconcat(*index_path, "-",
-						  my_hostname, NULL);
+			index_path = t_strconcat(index_path, "-",
+						 my_hostname, NULL);
 		}
 
-		if (rename(path, *index_path) < 0) {
+		if (rename(temp_path, index_path) < 0) {
 			index_set_error(index, "rename(%s, %s) failed: %m",
-					path, *index_path);
+					temp_path, index_path);
 			return FALSE;
 		}
 	}
 
+	index->filepath = i_strdup(index_path);
 	return TRUE;
 }
 
@@ -363,40 +373,37 @@
 			     int update_recent)
 {
 	MailIndexHeader hdr;
-	const char *path, *index_path;
-	int fd, nodiskspace;
+	const char *path;
+	int nodiskspace;
 
 	*dir_unlocked = FALSE;
-	index_path = NULL;
 
 	mail_index_init_header(index, &hdr);
 
 	if (index->nodiskspace) {
 		/* don't even bother trying to create it */
-		fd = -1;
 	} else {
 		/* first create the index into temporary file. */
-		fd = mail_index_create_temp_file(index, &path);
-		if (fd != -1) {
-			if (!mail_index_init_new_file(index, &hdr, fd,
-						      path, &index_path)) {
+		index->fd = mail_index_create_temp_file(index, &path);
+		if (index->fd != -1) {
+			if (!mail_index_init_new_file(index, &hdr, path)) {
 				int old_errno = errno;
 
-				(void)close(fd);
+				(void)close(index->fd);
 				(void)unlink(path);
-				fd = -1;
+				index->fd = -1;
 
 				errno = old_errno;
 			}
 		}
 
-		if (fd == -1 && errno != ENOSPC) {
+		if (index->fd == -1 && errno != ENOSPC) {
 			/* fatal failure */
 			return FALSE;
 		}
 	}
 
-	if (fd == -1) {
+	if (index->fd == -1) {
 		/* no space for index files, keep it in memory */
 		index->mmap_full_length = INDEX_FILE_MIN_SIZE;
 		index->mmap_base = mmap_anon(index->mmap_full_length);
@@ -407,11 +414,8 @@
 
 		index->anon_mmap = TRUE;
 		index->filepath = i_strdup("(in-memory index)");
-	} else {
-		index->filepath = i_strdup(index_path);
 	}
 
-	index->fd = fd;
 	index->indexid = hdr.indexid;
 
 	/* the fd is actually already locked, now we're just making it
@@ -548,18 +552,21 @@
 	if (mail_index_open(index, update_recent, fast))
 	        return TRUE;
 
+	if (index->index_lock_timeout || index->mailbox_lock_timeout)
+		return FALSE;
+
 	/* index wasn't found or it was broken. lock the directory and check
 	   again, just to make sure we don't end up having two index files
 	   due to race condition with another process. */
 	if (!mail_index_lock_dir(index, MAIL_LOCK_EXCLUSIVE))
 		return FALSE;
 
-	if (mail_index_open(index, update_recent, fast)) {
+	failed = FALSE;
+	if (mail_index_open(index, update_recent, fast))
 		dir_unlocked = FALSE;
-		failed = FALSE;
-	} else {
-		failed = !mail_index_create(index, &dir_unlocked,
-					    update_recent);
+	else if (!index->index_lock_timeout && !index->mailbox_lock_timeout) {
+		if (!mail_index_create(index, &dir_unlocked, update_recent))
+			failed = TRUE;
 	}
 
 	if (!dir_unlocked && !mail_index_lock_dir(index, MAIL_LOCK_UNLOCK))
--- a/src/lib-index/mail-index-util.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-index/mail-index-util.c	Mon Nov 25 21:02:49 2002 +0200
@@ -3,6 +3,7 @@
 #include "lib.h"
 #include "ibuffer.h"
 #include "hostpid.h"
+#include "file-lock.h"
 #include "message-size.h"
 #include "message-part-serialize.h"
 #include "mail-index.h"
@@ -105,3 +106,35 @@
 
 	return fd;
 }
+
+static void mail_index_lock_notify(unsigned int secs_left, void *context)
+{
+	MailIndex *index = context;
+
+	if (index->lock_notify_func == NULL)
+		return;
+
+	index->lock_notify_func(MAIL_LOCK_NOTIFY_INDEX_ABORT,
+				secs_left, index->lock_notify_context);
+}
+
+int mail_index_wait_lock(MailIndex *index, int lock_type)
+{
+	int ret;
+
+	ret = file_wait_lock_full(index->fd, lock_type, DEFAULT_LOCK_TIMEOUT,
+				  mail_index_lock_notify, index);
+	if (ret < 0)
+		return index_set_syscall_error(index, "file_wait_lock()");
+
+	if (ret == 0) {
+		index_set_error(index, "Timeout while waiting for "
+				"release of fcntl() lock for index file "
+				"%s", index->filepath);
+		index->index_lock_timeout = TRUE;
+		return FALSE;
+	}
+
+	return TRUE;
+
+}
--- a/src/lib-index/mail-index-util.h	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-index/mail-index-util.h	Mon Nov 25 21:02:49 2002 +0200
@@ -36,4 +36,7 @@
    and sets *path to the full path of the created file.  */
 int mail_index_create_temp_file(MailIndex *index, const char **path);
 
+/* Wrapper to file_set_lock(), also calling index's lock notify callback. */
+int mail_index_wait_lock(MailIndex *index, int lock_type);
+
 #endif
--- a/src/lib-index/mail-index.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-index/mail-index.c	Mon Nov 25 21:02:49 2002 +0200
@@ -258,8 +258,8 @@
 
 	/* use our own locking here so we don't mess up with any other
 	   index states, like inconsistency. */
-	if (file_wait_lock(index->fd, F_WRLCK, DEFAULT_LOCK_TIMEOUT) <= 0)
-		return index_set_syscall_error(index, "file_wait_lock()");
+	if (!mail_index_wait_lock(index, F_WRLCK))
+		return FALSE;
 
 #ifdef DEBUG
 	mprotect(index->mmap_base, index->mmap_used_length,
@@ -276,8 +276,8 @@
 	mprotect(index->mmap_base, index->mmap_used_length, PROT_NONE);
 #endif
 
-	if (file_wait_lock(index->fd, F_UNLCK, 0) <= 0)
-		return index_set_syscall_error(index, "file_wait_lock()");
+	if (!mail_index_wait_lock(index, F_UNLCK))
+		return FALSE;
 
 	return !failed;
 }
@@ -286,8 +286,8 @@
 {
 	MailLockType old_lock_type;
 
-	if (file_wait_lock(index->fd, F_UNLCK, 0) <= 0)
-		return index_set_syscall_error(index, "file_wait_lock()");
+	if (!mail_index_wait_lock(index, F_UNLCK))
+		return FALSE;
 
 	old_lock_type = index->lock_type;
 	index->lock_type = MAIL_LOCK_UNLOCK;
@@ -332,11 +332,8 @@
 		if (ret <= 0)
 			return FALSE;
 	} else {
-		if (file_wait_lock(index->fd, fd_lock_type,
-				   DEFAULT_LOCK_TIMEOUT) <= 0) {
-			return index_set_syscall_error(index,
-						       "file_wait_lock()");
-		}
+		if (!mail_index_wait_lock(index, fd_lock_type))
+			return FALSE;
 	}
 
 	index->lock_type = lock_type;
@@ -365,10 +362,8 @@
 			if (!mail_index_lock_remove(index))
 				return FALSE;
 
-			if (file_wait_lock(index->fd, MAIL_LOCK_EXCLUSIVE,
-					   DEFAULT_LOCK_TIMEOUT) <= 0)
-				return index_set_syscall_error(index,
-							"file_wait_lock()");
+			if (!mail_index_wait_lock(index, F_WRLCK))
+				return FALSE;
 			index->lock_type = MAIL_LOCK_EXCLUSIVE;
 
 			debug_mprotect(index->mmap_base,
@@ -458,6 +453,14 @@
 	return mail_index_lock_full(index, lock_type, TRUE);
 }
 
+void mail_index_set_lock_notify_callback(MailIndex *index,
+					 MailLockNotifyFunc func,
+					 void *context)
+{
+	index->lock_notify_func = func;
+	index->lock_notify_context = context;
+}
+
 int mail_index_verify_hole_range(MailIndex *index)
 {
 	MailIndexHeader *hdr;
@@ -983,17 +986,24 @@
 	return TRUE;
 }
 
-const char *mail_index_get_last_error(MailIndex *index)
+MailIndexError mail_index_get_last_error(MailIndex *index)
+{
+	if (index->inconsistent)
+		return MAIL_INDEX_ERROR_INCONSISTENT;
+	if (index->nodiskspace)
+		return MAIL_INDEX_ERROR_DISKSPACE;
+	if (index->index_lock_timeout)
+		return MAIL_INDEX_ERROR_INDEX_LOCK_TIMEOUT;
+	if (index->mailbox_lock_timeout)
+		return MAIL_INDEX_ERROR_MAILBOX_LOCK_TIMEOUT;
+
+	if (index->error != NULL)
+		return MAIL_INDEX_ERROR_INTERNAL;
+
+	return MAIL_INDEX_ERROR_NONE;
+}
+
+const char *mail_index_get_last_error_text(MailIndex *index)
 {
 	return index->error;
 }
-
-int mail_index_is_diskspace_error(MailIndex *index)
-{
-	return !index->inconsistent && index->nodiskspace;
-}
-
-int mail_index_is_inconsistency_error(MailIndex *index)
-{
-	return index->inconsistent;
-}
--- a/src/lib-index/mail-index.h	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-index/mail-index.h	Mon Nov 25 21:02:49 2002 +0200
@@ -67,6 +67,37 @@
 	MAIL_LOCK_EXCLUSIVE
 } MailLockType;
 
+typedef enum {
+	/* Mailbox is locked, will abort in secs_left */
+	MAIL_LOCK_NOTIFY_MAILBOX_ABORT,
+	/* Mailbox lock looks stale, will override in secs_left */
+	MAIL_LOCK_NOTIFY_MAILBOX_OVERRIDE,
+	/* Index is locked, will abort in secs_left */
+	MAIL_LOCK_NOTIFY_INDEX_ABORT
+} MailLockNotifyType;
+
+typedef enum {
+	/* No errors */
+	MAIL_INDEX_ERROR_NONE,
+	/* Internal error, see get_error_text() for more information. */
+	MAIL_INDEX_ERROR_INTERNAL,
+	/* Index is now in inconsistent state with the previous known state,
+	   meaning that the message IDs etc. may have changed - only way to
+	   recover this would be to fully close the mailbox and reopen it.
+	   With IMAP this would mean a forced disconnection since we can't do
+	   forced CLOSE. */
+	MAIL_INDEX_ERROR_INCONSISTENT,
+	/* We ran out of available disk space. */
+	MAIL_INDEX_ERROR_DISKSPACE,
+	/* Mail index locking timeouted */
+	MAIL_INDEX_ERROR_INDEX_LOCK_TIMEOUT,
+	/* Mailbox locking timeouted */
+	MAIL_INDEX_ERROR_MAILBOX_LOCK_TIMEOUT
+} MailIndexError;
+
+typedef void (*MailLockNotifyFunc)(MailLockNotifyType notify_type,
+				   unsigned int secs_left, void *context);
+
 typedef struct _MailIndex MailIndex;
 typedef struct _MailIndexData MailIndexData;
 typedef struct _MailTree MailTree;
@@ -179,7 +210,7 @@
 	   let it happen. Better ways to do this would be to a) mark the
 	   data to be updated later, b) use try_lock() if the update is
 	   preferred but not required, c) unlock + lock again, but make
-	   sure that won't create race conditions */
+	   sure that won't create race conditions. */
 	int (*set_lock)(MailIndex *index, MailLockType lock_type);
 
 	/* Try locking the index. Returns TRUE if the lock was got and
@@ -187,6 +218,12 @@
 	   occured. Never blocks. */
 	int (*try_lock)(MailIndex *index, MailLockType lock_type);
 
+	/* If we have to wait for the lock, the given lock notify function
+	   is called once in a while. */
+	void (*set_lock_notify_callback)(MailIndex *index,
+					 MailLockNotifyFunc func,
+					 void *context);
+
 	/* Rebuild the whole index. Note that this changes the indexid
 	   so all the other files must also be rebuilt after this call.
 	   Index MUST NOT have shared lock, but exclusive lock or no lock at
@@ -303,19 +340,12 @@
 	void (*update_field_raw)(MailIndexUpdate *update, MailDataField field,
 				 const void *value, size_t size);
 
-	/* Returns last error message */
-	const char *(*get_last_error)(MailIndex *index);
-
-	/* Returns TRUE if last error was because we ran out of available
-	   disk space. */
-	int (*is_diskspace_error)(MailIndex *index);
+	/* Returns the last error code. */
+	MailIndexError (*get_last_error)(MailIndex *index);
 
-	/* Returns TRUE if index is now in inconsistent state with the
-	   previous known state, meaning that the message IDs etc. may
-	   have changed - only way to recover this would be to fully close
-	   the mailbox and reopen it. With IMAP connection this would mean
-	   a forced disconnection since we can't do forced CLOSE. */
-	int (*is_inconsistency_error)(MailIndex *index);
+	/* Returns the full error message for last error. This message may
+	   contain paths etc. so it shouldn't be shown to users. */
+	const char *(*get_last_error_text)(MailIndex *index);
 
 /* private: */
 	MailIndexData *data;
@@ -359,6 +389,9 @@
 	time_t file_sync_stamp;
 	unsigned int first_recent_uid;
 
+	MailLockNotifyFunc lock_notify_func;
+	void *lock_notify_context;
+
 	/* these fields are OR'ed to the fields in index header once we
 	   get around grabbing exclusive lock */
 	unsigned int set_flags;
@@ -369,6 +402,8 @@
 	unsigned int mail_read_mmaped:1;
 	unsigned int inconsistent:1;
 	unsigned int nodiskspace:1;
+	unsigned int index_lock_timeout:1;
+	unsigned int mailbox_lock_timeout:1;
 };
 
 /* needed to remove annoying warnings about not initializing all struct
@@ -377,13 +412,17 @@
 	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, 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);
 int mail_index_open_or_create(MailIndex *index, int update_recent, int fast);
 int mail_index_set_lock(MailIndex *index, MailLockType lock_type);
 int mail_index_try_lock(MailIndex *index, MailLockType lock_type);
+void mail_index_set_lock_notify_callback(MailIndex *index,
+					 MailLockNotifyFunc func,
+					 void *context);
 int mail_index_fsck(MailIndex *index);
 MailIndexHeader *mail_index_get_header(MailIndex *index);
 MailIndexRecord *mail_index_lookup(MailIndex *index, unsigned int seq);
@@ -412,9 +451,8 @@
 void mail_index_update_field_raw(MailIndexUpdate *update, MailDataField field,
 				 const void *value, size_t size);
 time_t mail_get_internal_date(MailIndex *index, MailIndexRecord *rec);
-const char *mail_index_get_last_error(MailIndex *index);
-int mail_index_is_diskspace_error(MailIndex *index);
-int mail_index_is_inconsistency_error(MailIndex *index);
+MailIndexError mail_index_get_last_error(MailIndex *index);
+const char *mail_index_get_last_error_text(MailIndex *index);
 
 /* INTERNAL: */
 void mail_index_init(MailIndex *index, const char *dir);
--- a/src/lib-index/mail-modifylog.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-index/mail-modifylog.c	Mon Nov 25 21:02:49 2002 +0200
@@ -382,12 +382,12 @@
 	      this check to make sure it's not locked by others. */
 	ret = file_try_lock(fd, F_WRLCK);
 	if (ret < 0)
-		modifylog_set_syscall_error(file, "file_wait_lock()");
+		modifylog_set_syscall_error(file, "file_try_lock()");
 
 	if (ret > 0 && mail_modifylog_init_fd(file, fd)) {
 		/* drop back to read lock */
 		if (file_try_lock(fd, F_RDLCK) <= 0) {
-			modifylog_set_syscall_error(file, "file_wait_lock()");
+			modifylog_set_syscall_error(file, "file_try_lock()");
 			ret = -1;
 		}
 
@@ -417,7 +417,7 @@
 		return -1;
 	}
 
-	if (file_wait_lock(fd, F_RDLCK, DEFAULT_LOCK_TIMEOUT) <= 0) {
+	if (file_wait_lock(fd, F_RDLCK) <= 0) {
 		modifylog_set_syscall_error(file, "file_wait_lock()");
 		(void)close(fd);
 		return -1;
--- a/src/lib-index/maildir/maildir-index.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-index/maildir/maildir-index.c	Mon Nov 25 21:02:49 2002 +0200
@@ -233,6 +233,7 @@
 	maildir_index_free,
 	mail_index_set_lock,
 	mail_index_try_lock,
+        mail_index_set_lock_notify_callback,
 	maildir_index_rebuild,
 	mail_index_fsck,
 	maildir_index_sync,
@@ -254,8 +255,7 @@
 	mail_index_update_field,
 	mail_index_update_field_raw,
 	mail_index_get_last_error,
-	mail_index_is_diskspace_error,
-	mail_index_is_inconsistency_error,
+	mail_index_get_last_error_text,
 
 	MAIL_INDEX_PRIVATE_FILL
 };
--- a/src/lib-index/mbox/mbox-index.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-index/mbox/mbox-index.c	Mon Nov 25 21:02:49 2002 +0200
@@ -784,6 +784,7 @@
 	mbox_index_free,
 	mbox_index_set_lock,
 	mbox_index_try_lock,
+        mail_index_set_lock_notify_callback,
 	mbox_index_rebuild,
 	mail_index_fsck,
 	mbox_index_sync,
@@ -805,8 +806,7 @@
 	mail_index_update_field,
 	mail_index_update_field_raw,
 	mail_index_get_last_error,
-	mail_index_is_diskspace_error,
-	mail_index_is_inconsistency_error,
+	mail_index_get_last_error_text,
 
 	MAIL_INDEX_PRIVATE_FILL
 };
--- a/src/lib-index/mbox/mbox-lock.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-index/mbox/mbox-lock.c	Mon Nov 25 21:02:49 2002 +0200
@@ -66,6 +66,8 @@
 static int mbox_lock_flock(MailIndex *index, MailLockType lock_type,
 			   time_t max_wait_time)
 {
+	time_t now, last_notify;
+
 	if (lock_type == MAIL_LOCK_EXCLUSIVE)
 		lock_type = LOCK_EX;
 	else if (lock_type == MAIL_LOCK_SHARED)
@@ -73,18 +75,29 @@
 	else
 		lock_type = LOCK_UN;
 
+        last_notify = 0;
 	while (flock(index->mbox_fd, lock_type | LOCK_NB) < 0) {
 		if (errno != EWOULDBLOCK) {
-			index_file_set_syscall_error(index, index->mbox_path,
-						     "flock()");
+                        mbox_set_syscall_error(index, "flock()");
 			return FALSE;
 		}
 
-		if (max_wait_time != 0 && time(NULL) >= max_wait_time) {
-			errno = EAGAIN;
+		now = time(NULL);
+		if (max_wait_time != 0 && now >= max_wait_time) {
+			index->mailbox_lock_timeout = TRUE;
+			index_set_error(index, "Timeout while waiting for "
+					"release of flock() lock for mbox file "
+					"%s", index->mbox_path);
 			return FALSE;
 		}
 
+		if (now != last_notify && index->lock_notify_func != NULL) {
+			last_notify = now;
+			index->lock_notify_func(MAIL_LOCK_NOTIFY_MAILBOX_ABORT,
+						max_wait_time - now,
+						index->lock_notify_context);
+		}
+
 		usleep(LOCK_RANDOM_USLEEP_TIME);
 	}
 
@@ -96,6 +109,7 @@
 			   time_t max_wait_time)
 {
 	struct flock fl;
+	time_t now;
 
 	fl.l_type = MAIL_LOCK_TO_FLOCK(lock_type);
 	fl.l_whence = SEEK_SET;
@@ -104,15 +118,24 @@
 
 	while (fcntl(index->mbox_fd, F_SETLKW, &fl) == -1) {
 		if (errno != EINTR) {
-			index_file_set_syscall_error(index, index->mbox_path,
-						     "fcntl()");
+			mbox_set_syscall_error(index, "fcntl()");
 			return FALSE;
 		}
 
-		if (max_wait_time != 0 && time(NULL) >= max_wait_time) {
-			errno = EAGAIN;
+		now = time(NULL);
+		if (max_wait_time != 0 && now >= max_wait_time) {
+			index->mailbox_lock_timeout = TRUE;
+			index_set_error(index, "Timeout while waiting for "
+					"release of fcntl() lock for mbox file "
+					"%s", index->mbox_path);
 			return FALSE;
 		}
+
+		if (index->lock_notify_func != NULL) {
+			index->lock_notify_func(MAIL_LOCK_NOTIFY_MAILBOX_ABORT,
+						max_wait_time - now,
+						index->lock_notify_context);
+		}
 	}
 
 	return TRUE;
@@ -122,16 +145,19 @@
 			     time_t max_wait_time, int checkonly)
 {
 	struct stat st;
-	time_t now, last_change, last_mtime;
+	time_t now, last_change, last_notify, last_mtime, stale_notify;
 	off_t last_size;
+	unsigned int secs_left;
 	int fd;
 
 	path = t_strconcat(path, ".lock", NULL);
+	stale_notify = dotlock_change_timeout/2;
 
 	/* 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. */
-	last_change = time(NULL); last_size = 0; last_mtime = 0;
+	last_change = time(NULL); last_notify = 0;
+	last_size = 0; last_mtime = 0;
 	do {
 		now = time(NULL);
 
@@ -159,6 +185,23 @@
 				continue;
 			}
 
+			if (last_notify != now) {
+				last_notify = now;
+				if (now > last_change + stale_notify) {
+					secs_left = now - last_change +
+						dotlock_change_timeout;
+					index->lock_notify_func(
+					      MAIL_LOCK_NOTIFY_MAILBOX_OVERRIDE,
+					      secs_left,
+					      index->lock_notify_context);
+				} else {
+					index->lock_notify_func(
+						MAIL_LOCK_NOTIFY_MAILBOX_ABORT,
+						max_wait_time - now,
+						index->lock_notify_context);
+				}
+			}
+
 			usleep(LOCK_RANDOM_USLEEP_TIME);
 			continue;
 		}
@@ -203,6 +246,7 @@
 
 	index_set_error(index, "Timeout while waiting for release of mbox "
 			"dotlock %s", path);
+	index->mailbox_lock_timeout = TRUE;
 	return FALSE;
 }
 
--- a/src/lib-storage/index/index-copy.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/index/index-copy.c	Mon Nov 25 21:02:49 2002 +0200
@@ -66,8 +66,8 @@
 
 	if (ctx.copy_inside_mailbox) {
 		/* copying inside same mailbox */
-		if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_EXCLUSIVE))
-			return mail_storage_set_index_error(ibox);
+		if (!index_storage_lock(ibox, MAIL_LOCK_EXCLUSIVE))
+			return FALSE;
 
 		lock_type = MAIL_LOCK_EXCLUSIVE;
 	} else {
@@ -84,8 +84,8 @@
 	failed = index_messageset_foreach(ibox, messageset, uidset,
 					  copy_func, &ctx) <= 0;
 
-	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_UNLOCK))
-		return mail_storage_set_index_error(ibox);
+	if (!index_storage_lock(ibox, MAIL_LOCK_UNLOCK))
+		return FALSE;
 
 	return !failed;
 }
--- a/src/lib-storage/index/index-expunge.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/index/index-expunge.c	Mon Nov 25 21:02:49 2002 +0200
@@ -51,8 +51,9 @@
 
 	if (seq <= ibox->synced_messages_count) {
 		if (notify) {
-			ibox->sync_callbacks.expunge(&ibox->box, seq,
-						     ibox->sync_context);
+			MailStorage *storage = ibox->box.storage;
+			storage->callbacks->expunge(&ibox->box, seq,
+						    storage->callback_context);
 		}
 		ibox->synced_messages_count--;
 	}
@@ -70,8 +71,8 @@
 		return FALSE;
 	}
 
-	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_EXCLUSIVE))
-		return mail_storage_set_index_error(ibox);
+	if (!index_storage_lock(ibox, MAIL_LOCK_EXCLUSIVE))
+		return FALSE;
 
 	if (!index_storage_sync_and_lock(ibox, FALSE, MAIL_LOCK_EXCLUSIVE))
 		return FALSE;
@@ -83,8 +84,8 @@
 	else
 		failed = !ibox->expunge_locked(ibox, notify);
 
-	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_UNLOCK))
-		return mail_storage_set_index_error(ibox);
+	if (!index_storage_lock(ibox, MAIL_LOCK_UNLOCK))
+		return FALSE;
 
 	return !failed;
 }
--- a/src/lib-storage/index/index-fetch.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/index/index-fetch.c	Mon Nov 25 21:02:49 2002 +0200
@@ -366,8 +366,8 @@
 
 	/* need exclusive lock to update the \Seen flags */
 	if (ctx.update_seen) {
-		if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_EXCLUSIVE))
-			return mail_storage_set_index_error(ibox);
+		if (!index_storage_lock(ibox, MAIL_LOCK_EXCLUSIVE))
+			return FALSE;
 	}
 
 	if (!index_storage_sync_and_lock(ibox, TRUE, MAIL_LOCK_SHARED))
@@ -379,7 +379,7 @@
 		/* if all messages are already seen, there's no point in
 		   keeping exclusive lock */
 		ctx.update_seen = FALSE;
-		(void)ibox->index->set_lock(ibox->index, MAIL_LOCK_SHARED);
+		(void)index_storage_lock(ibox, MAIL_LOCK_SHARED);
 	}
 
 	ctx.box = box;
@@ -397,8 +397,8 @@
 				       fetch_data->uidset,
 				       index_fetch_mail, &ctx);
 
-	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_UNLOCK))
-		return mail_storage_set_index_error(ibox);
+	if (!index_storage_lock(ibox, MAIL_LOCK_UNLOCK))
+		return FALSE;
 
 	if (all_found != NULL)
 		*all_found = ret == 1 && !ctx.failed;
--- a/src/lib-storage/index/index-messageset.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/index/index-messageset.c	Mon Nov 25 21:02:49 2002 +0200
@@ -85,7 +85,8 @@
 		rec = index->next(index, rec);
 	}
 
-	if (rec == NULL && index->get_last_error(index) != NULL) {
+	if (rec == NULL &&
+	    index->get_last_error(index) != MAIL_INDEX_ERROR_NONE) {
 		/* error occured */
 		return -1;
 	}
@@ -230,7 +231,8 @@
 		rec = index->next(index, rec);
 	}
 
-	if (rec == NULL && index->get_last_error(index) != NULL) {
+	if (rec == NULL &&
+	    index->get_last_error(index) != MAIL_INDEX_ERROR_NONE) {
 		/* error occured */
 		return -1;
 	}
--- a/src/lib-storage/index/index-search.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/index/index-search.c	Mon Nov 25 21:02:49 2002 +0200
@@ -849,8 +849,8 @@
 	failed = !search_messages(ibox, charset, args, outbuf, uid_result);
 	o_buffer_send(outbuf, "\r\n", 2);
 
-	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_UNLOCK))
-		return mail_storage_set_index_error(ibox);
+	if (!index_storage_lock(ibox, MAIL_LOCK_UNLOCK))
+		return FALSE;
 
 	return !failed;
 }
--- a/src/lib-storage/index/index-status.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/index/index-status.c	Mon Nov 25 21:02:49 2002 +0200
@@ -79,8 +79,7 @@
 		return FALSE;
 
 	if (!index_storage_sync_modifylog(ibox, FALSE)) {
-		if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_UNLOCK))
-			return mail_storage_set_index_error(ibox);
+		(void)index_storage_lock(ibox, MAIL_LOCK_UNLOCK);
 		return FALSE;
 	}
 
@@ -103,7 +102,7 @@
 	if (items & STATUS_CUSTOM_FLAGS)
 		get_custom_flags(ibox->index->custom_flags, status);
 
-	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_UNLOCK))
-		return mail_storage_set_index_error(ibox);
+	if (!index_storage_lock(ibox, MAIL_LOCK_UNLOCK))
+		return FALSE;
 	return TRUE;
 }
--- a/src/lib-storage/index/index-storage.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/index/index-storage.c	Mon Nov 25 21:02:49 2002 +0200
@@ -7,9 +7,12 @@
 #include "index-storage.h"
 
 #include <stdlib.h>
+#include <time.h>
 #include <unistd.h>
 #include <sys/stat.h>
 
+#define LOCK_NOTIFY_INTERVAL 30
+
 typedef struct _IndexList IndexList;
 
 struct _IndexList {
@@ -140,37 +143,77 @@
 	return ret;
 }
 
+static void lock_notify(MailLockNotifyType notify_type,
+			unsigned int secs_left, void *context)
+{
+	IndexMailbox *ibox = context;
+	MailStorage *storage = ibox->box.storage;
+	const char *str;
+	time_t now;
+
+	if ((secs_left % 15) != 0) {
+		/* update alarm() so that we get back here around the same
+		   time we want the next notify. also try to use somewhat
+		   rounded times. this affects only fcntl() locking, dotlock
+		   and flock() calls should be calling us constantly */
+		alarm(secs_left%15);
+	}
+
+	now = time(NULL);
+	if (now < ibox->next_lock_notify || secs_left < 15)
+		return;
+
+	ibox->next_lock_notify = now + LOCK_NOTIFY_INTERVAL;
+
+	switch (notify_type) {
+	case MAIL_LOCK_NOTIFY_MAILBOX_ABORT:
+		str = t_strdup_printf("Mailbox is locked, will abort in "
+				      "%u seconds", secs_left);
+		storage->callbacks->notify_no(&ibox->box, str,
+					      storage->callback_context);
+		break;
+	case MAIL_LOCK_NOTIFY_MAILBOX_OVERRIDE:
+		str = t_strdup_printf("Stale mailbox lock file detected, "
+				      "will override in %u seconds", secs_left);
+		storage->callbacks->notify_ok(&ibox->box, str,
+					      storage->callback_context);
+		break;
+	case MAIL_LOCK_NOTIFY_INDEX_ABORT:
+		str = t_strdup_printf("Mailbox index is locked, will abort in "
+				      "%u seconds", secs_left);
+		storage->callbacks->notify_no(&ibox->box, str,
+					      storage->callback_context);
+		break;
+	}
+}
+
+int index_storage_lock(IndexMailbox *ibox, MailLockType lock_type)
+{
+	int ret;
+
+	ibox->next_lock_notify = time(NULL) + LOCK_NOTIFY_INTERVAL;
+
+	/* we have to set/reset this every time, because the same index
+	   may be used by multiple IndexMailboxes. */
+	ibox->index->set_lock_notify_callback(ibox->index, lock_notify, ibox);
+	ret = ibox->index->set_lock(ibox->index, lock_type);
+	ibox->index->set_lock_notify_callback(ibox->index, NULL, NULL);
+
+	if (!ret)
+		return mail_storage_set_index_error(ibox);
+
+	return TRUE;
+}
+
 IndexMailbox *index_storage_init(MailStorage *storage, Mailbox *box,
 				 MailIndex *index, const char *name,
 				 int readonly, int fast)
 {
 	IndexMailbox *ibox;
-	MailIndexHeader *hdr;
-	unsigned int messages;
 
 	i_assert(name != NULL);
 
 	do {
-		if (!index->opened) {
-			/* open the index first */
-			index->default_cache_fields =
-				get_default_cache_fields();
-			index->never_cache_fields =
-				get_never_cache_fields();
-			if (!index->open_or_create(index, !readonly, fast))
-				break;
-		}
-
-		/* Get the synced messages count */
-		if (!index->set_lock(index, MAIL_LOCK_SHARED))
-			break;
-
-		hdr = mail_index_get_header(index);
-		messages = hdr->messages_count;
-
-		if (!index->set_lock(index, MAIL_LOCK_UNLOCK))
-			break;
-
 		ibox = i_new(IndexMailbox, 1);
 		ibox->box = *box;
 
@@ -181,13 +224,36 @@
 
 		ibox->index = index;
 		ibox->cache = imap_msgcache_alloc(&index_msgcache_iface);
-		ibox->synced_messages_count = messages;
+
+		ibox->next_lock_notify = time(NULL) + LOCK_NOTIFY_INTERVAL;
+		index->set_lock_notify_callback(index, lock_notify, ibox);
+
+		if (!index->opened) {
+			/* open the index first */
+			index->default_cache_fields =
+				get_default_cache_fields();
+			index->never_cache_fields =
+				get_never_cache_fields();
+			if (!index->open_or_create(index, !readonly, fast))
+				break;
+		}
+
+		if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_SHARED))
+			break;
+
+		ibox->synced_messages_count =
+			mail_index_get_header(index)->messages_count;
+
+		if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_UNLOCK))
+			break;
+
+		index->set_lock_notify_callback(index, NULL, NULL);
 
 		return ibox;
 	} while (0);
 
-	mail_storage_set_internal_error(storage);
-	index_storage_unref(index);
+	mail_storage_set_index_error(ibox);
+	index_storage_close(&ibox->box);
 	return NULL;
 }
 
@@ -197,32 +263,48 @@
 
 	index_mailbox_check_remove(ibox);
 	imap_msgcache_free(ibox->cache);
-	index_storage_unref(ibox->index);
+	if (ibox->index != NULL)
+		index_storage_unref(ibox->index);
+
 	i_free(box->name);
 	i_free(box);
 
 	return TRUE;
 }
 
-void index_storage_set_sync_callbacks(Mailbox *box,
-				      MailboxSyncCallbacks *callbacks,
-				      void *context)
+void index_storage_set_callbacks(MailStorage *storage,
+				 MailStorageCallbacks *callbacks,
+				 void *context)
 {
-	IndexMailbox *ibox = (IndexMailbox *) box;
-
-	memcpy(&ibox->sync_callbacks, callbacks, sizeof(MailboxSyncCallbacks));
-	ibox->sync_context = context;
+	memcpy(storage->callbacks, callbacks, sizeof(MailStorageCallbacks));
+	storage->callback_context = context;
 }
 
 int mail_storage_set_index_error(IndexMailbox *ibox)
 {
-	ibox->box.inconsistent =
-		ibox->index->is_inconsistency_error(ibox->index);
+	switch (ibox->index->get_last_error(ibox->index)) {
+	case MAIL_INDEX_ERROR_NONE:
+	case MAIL_INDEX_ERROR_INTERNAL:
+		mail_storage_set_internal_error(ibox->box.storage);
+		break;
+	case MAIL_INDEX_ERROR_INCONSISTENT:
+		ibox->box.inconsistent = TRUE;
+		break;
+	case MAIL_INDEX_ERROR_DISKSPACE:
+		mail_storage_set_error(ibox->box.storage, "Out of disk space");
+		break;
+	case MAIL_INDEX_ERROR_INDEX_LOCK_TIMEOUT:
+		mail_storage_set_error(ibox->box.storage, t_strconcat(
+			"Timeout while waiting for lock to index of mailbox ",
+			ibox->box.name, NULL));
+		break;
+	case MAIL_INDEX_ERROR_MAILBOX_LOCK_TIMEOUT:
+		mail_storage_set_error(ibox->box.storage, t_strconcat(
+			"Timeout while waiting for lock to mailbox ",
+			ibox->box.name, NULL));
+		break;
+	}
 
-	if (ibox->index->is_diskspace_error(ibox->index))
-		mail_storage_set_error(ibox->box.storage, "Out of disk space");
-	else
-		mail_storage_set_internal_error(ibox->box.storage);
 	index_reset_error(ibox->index);
 	return FALSE;
 }
--- a/src/lib-storage/index/index-storage.h	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/index/index-storage.h	Mon Nov 25 21:02:49 2002 +0200
@@ -14,9 +14,6 @@
 	   exclusively locked */
 	int (*expunge_locked)(IndexMailbox *ibox, int notify);
 
-        MailboxSyncCallbacks sync_callbacks;
-	void *sync_context;
-
 	MailIndex *index;
 	ImapMessageCache *cache;
 
@@ -27,6 +24,8 @@
 
 	unsigned int synced_messages_count;
 
+	time_t next_lock_notify; /* temporary */
+
 	unsigned int sent_diskspace_warning:1;
 	unsigned int delay_save_unlocking:1; /* For COPYing inside mailbox */
 };
@@ -34,6 +33,7 @@
 extern ImapMessageCacheIface index_msgcache_iface;
 
 int mail_storage_set_index_error(IndexMailbox *ibox);
+int index_storage_lock(IndexMailbox *ibox, MailLockType lock_type);
 
 void index_storage_add(MailIndex *index);
 MailIndex *index_storage_lookup_ref(const char *path);
@@ -68,9 +68,9 @@
 void index_mailbox_check_remove(IndexMailbox *ibox);
 
 /* Mailbox methods: */
-void index_storage_set_sync_callbacks(Mailbox *box,
-				      MailboxSyncCallbacks *callbacks,
-				      void *context);
+void index_storage_set_callbacks(MailStorage *storage,
+				 MailStorageCallbacks *callbacks,
+				 void *context);
 int index_storage_copy(Mailbox *box, Mailbox *destbox,
 		       const char *messageset, int uidset);
 int index_storage_expunge(Mailbox *box, int notify);
--- a/src/lib-storage/index/index-sync.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/index/index-sync.c	Mon Nov 25 21:02:49 2002 +0200
@@ -9,9 +9,10 @@
 
 static void index_storage_sync_size(IndexMailbox *ibox)
 {
+	MailStorage *storage = ibox->box.storage;
 	unsigned int messages, recent;
 
-	if (ibox->sync_callbacks.new_messages == NULL)
+	if (storage->callbacks->new_messages == NULL)
 		return;
 
 	messages = ibox->index->get_header(ibox->index)->messages_count;
@@ -22,8 +23,8 @@
 
 		/* new messages in mailbox */
 		recent = index_storage_get_recent_count(ibox->index);
-		ibox->sync_callbacks.new_messages(&ibox->box, messages, recent,
-						  ibox->sync_context);
+		storage->callbacks->new_messages(&ibox->box, messages, recent,
+						 storage->callback_context);
 		ibox->synced_messages_count = messages;
 	}
 }
@@ -31,6 +32,7 @@
 int index_storage_sync_and_lock(IndexMailbox *ibox, int sync_size,
 				MailLockType data_lock_type)
 {
+	MailStorage *storage = ibox->box.storage;
 	MailIndex *index = ibox->index;
 	int changes, set_shared_lock;
 
@@ -40,17 +42,18 @@
 		/* reset every time it has worked */
 		ibox->sent_diskspace_warning = FALSE;
 	} else {
-		if (!index->is_diskspace_error(index)) {
-			(void)index->set_lock(index, MAIL_LOCK_UNLOCK);
+		if (index->get_last_error(index) !=
+		    MAIL_INDEX_ERROR_DISKSPACE) {
+			(void)index_storage_lock(ibox, MAIL_LOCK_UNLOCK);
 			return mail_storage_set_index_error(ibox);
 		}
 
 		/* notify client once about it */
 		if (!ibox->sent_diskspace_warning &&
-		    ibox->sync_callbacks.alert_no_diskspace != NULL) {
+		    storage->callbacks->alert_no_diskspace != NULL) {
 			ibox->sent_diskspace_warning = TRUE;
-			ibox->sync_callbacks.alert_no_diskspace(
-						&ibox->box, ibox->sync_context);
+			storage->callbacks->alert_no_diskspace(
+				&ibox->box, storage->callback_context);
 		}
 
 		index_reset_error(index);
@@ -59,8 +62,8 @@
 	if (set_shared_lock) {
 		/* just make sure we are locked, and that we drop our
 		   exclusive lock if it wasn't wanted originally */
-		if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_SHARED)) {
-			(void)index->set_lock(index, MAIL_LOCK_UNLOCK);
+		if (!index_storage_lock(ibox, MAIL_LOCK_SHARED)) {
+			(void)index_storage_lock(ibox, MAIL_LOCK_UNLOCK);
 			return FALSE;
 		}
 	}
@@ -74,10 +77,10 @@
 
 	/* notify changes in custom flags */
 	if (mail_custom_flags_has_changes(index->custom_flags) &&
-	    ibox->sync_callbacks.new_custom_flags != NULL) {
-		ibox->sync_callbacks.new_custom_flags(&ibox->box,
+	    storage->callbacks->new_custom_flags != NULL) {
+		storage->callbacks->new_custom_flags(&ibox->box,
                 	mail_custom_flags_list_get(index->custom_flags),
-			MAIL_CUSTOM_FLAGS_COUNT, ibox->sync_context);
+			MAIL_CUSTOM_FLAGS_COUNT, storage->callback_context);
 	}
 
 	return TRUE;
@@ -88,7 +91,7 @@
 	const ModifyLogRecord *log1, *log2, *log, *first_flag_log;
 	MailIndexRecord *rec;
 	MailFlags flags;
-        MailboxSyncCallbacks *sc;
+        MailStorageCallbacks *sc;
 	void *sc_context;
 	const char **custom_flags;
 	unsigned int count1, count2, total_count, seq, seq_count, i, messages;
@@ -99,8 +102,8 @@
 					  &log1, &count1, &log2, &count2))
 		return mail_storage_set_index_error(ibox);
 
-	sc = &ibox->sync_callbacks;
-	sc_context = ibox->sync_context;
+	sc = ibox->box.storage->callbacks;
+	sc_context = ibox->box.storage->callback_context;
 
 	/* first show expunges. this makes it easier to deal with sequence
 	   numbers. */
@@ -222,8 +225,8 @@
 
 	index_storage_sync_size(ibox);
 
-	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_UNLOCK))
-		return mail_storage_set_index_error(ibox);
+	if (!index_storage_lock(ibox, MAIL_LOCK_UNLOCK))
+		return FALSE;
 
 	return ret;
 }
--- a/src/lib-storage/index/index-update-flags.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/index/index-update-flags.c	Mon Nov 25 21:02:49 2002 +0200
@@ -17,6 +17,7 @@
 		       void *context)
 {
 	UpdateContext *ctx = context;
+	MailStorage *storage;
 	MailFlags flags;
 	const char **custom_flags;
 
@@ -37,10 +38,11 @@
 	if (!index->update_flags(index, rec, idx_seq, flags, FALSE))
 		return FALSE;
 
+	storage = ctx->ibox->box.storage;
 	if (mail_custom_flags_has_changes(index->custom_flags)) {
-		ctx->ibox->sync_callbacks.new_custom_flags(&ctx->ibox->box,
+		storage->callbacks->new_custom_flags(&ctx->ibox->box,
 			mail_custom_flags_list_get(index->custom_flags),
-			MAIL_CUSTOM_FLAGS_COUNT, ctx->ibox->sync_context);
+			MAIL_CUSTOM_FLAGS_COUNT, storage->callback_context);
 	}
 
 	if (ctx->notify) {
@@ -48,11 +50,11 @@
 			flags |= MAIL_RECENT;
 
                 custom_flags = mail_custom_flags_list_get(index->custom_flags);
-		ctx->ibox->sync_callbacks.update_flags(&ctx->ibox->box,
-						       client_seq, rec->uid,
-						       flags, custom_flags,
-						       MAIL_CUSTOM_FLAGS_COUNT,
-						       ctx->ibox->sync_context);
+		storage->callbacks->update_flags(&ctx->ibox->box,
+						 client_seq, rec->uid,
+						 flags, custom_flags,
+						 MAIL_CUSTOM_FLAGS_COUNT,
+						 storage->callback_context);
 	}
 
 	return TRUE;
@@ -75,8 +77,8 @@
 	if (!index_mailbox_fix_custom_flags(ibox, &flags, custom_flags))
 		return FALSE;
 
-	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_EXCLUSIVE))
-		return mail_storage_set_index_error(ibox);
+	if (!index_storage_lock(ibox, MAIL_LOCK_EXCLUSIVE))
+		return FALSE;
 
 	if (!index_storage_sync_and_lock(ibox, TRUE, MAIL_LOCK_UNLOCK))
 		return FALSE;
@@ -89,8 +91,8 @@
 	ret = index_messageset_foreach(ibox, messageset, uidset,
 				       update_func, &ctx);
 
-	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_UNLOCK))
-		return mail_storage_set_index_error(ibox);
+	if (!index_storage_lock(ibox, MAIL_LOCK_UNLOCK))
+		return FALSE;
 
 	if (all_found != NULL)
 		*all_found = ret == 1;
--- a/src/lib-storage/index/maildir/maildir-copy.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/index/maildir/maildir-copy.c	Mon Nov 25 21:02:49 2002 +0200
@@ -69,8 +69,7 @@
 	ret = index_messageset_foreach(src, messageset, uidset,
 				       copy_hard_func, &ctx);
 
-	if (!src->index->set_lock(src->index, MAIL_LOCK_UNLOCK))
-		mail_storage_set_index_error(src);
+	(void)index_storage_lock(src, MAIL_LOCK_UNLOCK);
 
 	return ctx.error ? -1 : ret;
 }
--- a/src/lib-storage/index/maildir/maildir-storage.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/index/maildir/maildir-storage.c	Mon Nov 25 21:02:49 2002 +0200
@@ -53,11 +53,13 @@
 
 	storage->dir = i_strdup(data);
 	storage->user = i_strdup(user);
+	storage->callbacks = i_new(MailStorageCallbacks, 1);
 	return storage;
 }
 
 static void maildir_free(MailStorage *storage)
 {
+	i_free(storage->callbacks);
 	i_free(storage->dir);
 	i_free(storage->user);
 	i_free(storage);
@@ -438,6 +440,7 @@
 	maildir_create,
 	maildir_free,
 	maildir_autodetect,
+	index_storage_set_callbacks,
 	maildir_open_mailbox,
 	maildir_create_mailbox,
 	maildir_delete_mailbox,
@@ -450,7 +453,8 @@
 
 	NULL,
 	NULL,
-	NULL
+	NULL,
+	NULL, NULL
 };
 
 Mailbox maildir_mailbox = {
@@ -458,7 +462,6 @@
 	NULL, /* storage */
 
 	index_storage_close,
-	index_storage_set_sync_callbacks,
 	index_storage_get_status,
 	index_storage_sync,
 	index_storage_expunge,
--- a/src/lib-storage/index/mbox/mbox-save.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/index/mbox/mbox-save.c	Mon Nov 25 21:02:49 2002 +0200
@@ -218,8 +218,8 @@
 
 	/* kludgy.. for copying inside same mailbox. */
 	if (!ibox->delay_save_unlocking) {
-		if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_UNLOCK))
-			return mail_storage_set_index_error(ibox);
+		if (!index_storage_lock(ibox, MAIL_LOCK_UNLOCK))
+			return FALSE;
 	}
 
 	return !failed;
--- a/src/lib-storage/index/mbox/mbox-storage.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/index/mbox/mbox-storage.c	Mon Nov 25 21:02:49 2002 +0200
@@ -76,11 +76,13 @@
 
 	storage->dir = i_strdup(data);
 	storage->user = i_strdup(user);
+	storage->callbacks = i_new(MailStorageCallbacks, 1);
 	return storage;
 }
 
 static void mbox_free(MailStorage *storage)
 {
+	i_free(storage->callbacks);
 	i_free(storage->dir);
 	i_free(storage->user);
 	i_free(storage);
@@ -397,6 +399,7 @@
 	mbox_create,
 	mbox_free,
 	mbox_autodetect,
+	index_storage_set_callbacks,
 	mbox_open_mailbox,
 	mbox_create_mailbox,
 	mbox_delete_mailbox,
@@ -409,7 +412,8 @@
 
 	NULL,
 	NULL,
-	NULL
+	NULL,
+	NULL, NULL
 };
 
 Mailbox mbox_mailbox = {
@@ -417,7 +421,6 @@
 	NULL, /* storage */
 
 	mbox_storage_close,
-	index_storage_set_sync_callbacks,
 	index_storage_get_status,
 	index_storage_sync,
 	index_storage_expunge,
--- a/src/lib-storage/mail-storage.h	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/mail-storage.h	Mon Nov 25 21:02:49 2002 +0200
@@ -40,7 +40,7 @@
 typedef struct _MailStorage MailStorage;
 typedef struct _Mailbox Mailbox;
 typedef struct _MailboxStatus MailboxStatus;
-typedef struct _MailboxSyncCallbacks MailboxSyncCallbacks;
+typedef struct _MailStorageCallbacks MailStorageCallbacks;
 typedef struct _MailFetchData MailFetchData;
 typedef struct _MailFetchBodyData MailFetchBodyData;
 typedef struct _MailSearchArg MailSearchArg;
@@ -64,6 +64,10 @@
 	   as a valid parameter to create(). */
 	int (*autodetect)(const char *data);
 
+	/* Set storage callback functions to use. */
+	void (*set_callbacks)(MailStorage *storage,
+			      MailStorageCallbacks *callbacks, void *context);
+
 	/* Open a mailbox. If readonly is TRUE, mailbox must not be
 	   modified in any way even when it's asked. If fast is TRUE,
 	   any extra time consuming operations shouldn't be performed
@@ -112,6 +116,9 @@
 	char *dir; /* root directory */
 	char *user; /* name of user accessing the storage */
 	char *error;
+
+	MailStorageCallbacks *callbacks;
+	void *callback_context;
 };
 
 struct _Mailbox {
@@ -123,11 +130,6 @@
 	   the mailbox was closed anyway. */
 	int (*close)(Mailbox *box);
 
-	/* Set synchronization callback functions to use. */
-	void (*set_sync_callbacks)(Mailbox *box,
-				   MailboxSyncCallbacks *callbacks,
-				   void *context);
-
 	/* Gets the mailbox status information. */
 	int (*get_status)(Mailbox *box, MailboxStatusItems items,
 			  MailboxStatus *status);
@@ -199,9 +201,13 @@
 	const char **custom_flags;
 };
 
-struct _MailboxSyncCallbacks {
+struct _MailStorageCallbacks {
 	/* Alert: Not enough disk space */
 	void (*alert_no_diskspace)(Mailbox *mailbox, void *context);
+	/* "* OK <text>" */
+	void (*notify_ok)(Mailbox *mailbox, const char *text, void *context);
+	/* "* NO <text>" */
+	void (*notify_no)(Mailbox *mailbox, const char *text, void *context);
 
 	/* EXPUNGE */
 	void (*expunge)(Mailbox *mailbox, unsigned int seq, void *context);
--- a/src/lib-storage/subscription-file/subscription-file.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib-storage/subscription-file/subscription-file.c	Mon Nov 25 21:02:49 2002 +0200
@@ -42,8 +42,9 @@
 		return -1;
 	}
 
-	if (file_wait_lock(fd, update ? F_WRLCK : F_RDLCK,
-			   DEFAULT_LOCK_TIMEOUT) <= 0) {
+	/* FIXME: we should work without locking, rename() would be easiest
+	   but .lock would work too */
+	if (file_wait_lock(fd, update ? F_WRLCK : F_RDLCK) <= 0) {
 		subsfile_set_syscall_error(storage, "file_wait_lock()", *path);
 		(void)close(fd);
 		return -1;
--- a/src/lib/file-lock.c	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib/file-lock.c	Mon Nov 25 21:02:49 2002 +0200
@@ -30,11 +30,12 @@
 #include <time.h>
 #include <signal.h>
 
-static int file_lock(int fd, int wait_lock, int lock_type,
-		     unsigned int timeout)
+static int file_lock(int fd, int wait_lock, int lock_type, unsigned int timeout,
+		     void (*func)(unsigned int secs_left, void *context),
+		     void *context)
 {
 	struct flock fl;
-	time_t timeout_time;
+	time_t timeout_time, now;
 
 	if (timeout == 0)
 		timeout_time = 0;
@@ -55,10 +56,14 @@
 		if (errno != EINTR)
 			return -1;
 
-		if (timeout != 0 && time(NULL) >= timeout_time) {
+		now = time(NULL);
+		if (timeout != 0 && now >= timeout_time) {
 			errno = EAGAIN;
 			return 0;
 		}
+
+		if (func != NULL)
+			func(timeout_time - now, context);
 	}
 
 	return 1;
@@ -66,13 +71,18 @@
 
 int file_try_lock(int fd, int lock_type)
 {
-	return file_lock(fd, FALSE, lock_type, 0);
+	return file_lock(fd, FALSE, lock_type, 0, NULL, NULL);
 }
 
-int file_wait_lock(int fd, int lock_type, unsigned int timeout)
+int file_wait_lock(int fd, int lock_type)
 {
-	int ret;
+	return file_lock(fd, FALSE, lock_type, DEFAULT_LOCK_TIMEOUT,
+			 NULL, NULL);
+}
 
-	ret = file_lock(fd, TRUE, lock_type, timeout);
-	return ret;
+int file_wait_lock_full(int fd, int lock_type, unsigned int timeout,
+			void (*func)(unsigned int secs_left, void *context),
+			void *context)
+{
+	return file_lock(fd, TRUE, lock_type, timeout, func, context);
 }
--- a/src/lib/file-lock.h	Mon Nov 25 17:00:53 2002 +0200
+++ b/src/lib/file-lock.h	Mon Nov 25 21:02:49 2002 +0200
@@ -11,8 +11,14 @@
 int file_try_lock(int fd, int lock_type);
 
 /* Lock whole file descriptor. Returns 1 if successful, 0 if timeout or
-   -1 if error. When returning 0, errno is also set to EAGAIN.
-   NOTE: timeout uses SIGALRM and resets it at the end. */
-int file_wait_lock(int fd, int lock_type, unsigned int timeout);
+   -1 if error. When returning 0, errno is also set to EAGAIN. Timeouts after
+   DEFAULT_LOCK_TIMEOUT. */
+int file_wait_lock(int fd, int lock_type);
+
+/* Like file_wait_lock(), but you can specify the timout and a function which
+   is called once in a while if waiting takes longer. */
+int file_wait_lock_full(int fd, int lock_type, unsigned int timeout,
+			void (*func)(unsigned int secs_left, void *context),
+			void *context);
 
 #endif