changeset 223:ca6967899c05 HEAD

More cleanups. lib-storage should handle "out of disk space" conditions properly.
author Timo Sirainen <tss@iki.fi>
date Sun, 15 Sep 2002 09:30:29 +0300
parents cf4d065f2f85
children e8863d6088fe
files src/lib-index/mail-hash.c src/lib-index/mail-hash.h src/lib-index/mail-index-compress.c src/lib-index/mail-index-data.c src/lib-index/mail-index-data.h src/lib-index/mail-index.c src/lib-index/mail-lockdir.c src/lib-index/mail-modifylog.c src/lib-storage/index/index-copy.c src/lib-storage/index/index-fetch.c src/lib-storage/index/index-save.c src/lib-storage/index/index-search.c src/lib-storage/index/index-status.c src/lib-storage/index/index-storage.h src/lib-storage/index/index-sync.c src/lib-storage/index/maildir/maildir-save.c src/lib-storage/index/mbox/mbox-save.c src/lib/write-full.h
diffstat 18 files changed, 338 insertions(+), 175 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-index/mail-hash.c	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-index/mail-hash.c	Sun Sep 15 09:30:29 2002 +0300
@@ -57,6 +57,31 @@
 	unsigned int modified:1;
 };
 
+static void mail_hash_file_close(MailHash *hash)
+{
+	if (close(hash->fd) < 0) {
+		index_file_set_syscall_error(hash->index,
+					     hash->filepath, "close()");
+	}
+	hash->fd = -1;
+}
+
+static int mail_hash_file_open(MailHash *hash)
+{
+	hash->fd = open(hash->filepath, O_RDWR);
+	if (hash->fd == -1) {
+		if (errno != ENOENT) {
+			index_file_set_syscall_error(hash->index,
+						     hash->filepath, "open()");
+			return FALSE;
+		}
+
+		return mail_hash_rebuild(hash);
+	}
+
+	return TRUE;
+}
+
 static int mmap_update_real(MailHash *hash)
 {
 	i_assert(!hash->anon_mmap);
@@ -91,7 +116,6 @@
 	}
 
 	hash->header = hash->mmap_base;
-	hash->sync_id = hash->header->sync_id;
 	hash->size = (hash->mmap_length - sizeof(MailHashHeader)) /
 		sizeof(MailHashRecord);
 
@@ -110,14 +134,26 @@
 static int mmap_update(MailHash *hash)
 {
 	if (hash->fd == -1)
-		return FALSE;
+		return hash->anon_mmap;
+
+	/* see if it's been rebuilt */
+	if (hash->header->indexid == hash->index->indexid)
+		return TRUE;
 
-	if (!hash->dirty_mmap) {
-		/* see if someone else modified it */
-		if (hash->header->sync_id == hash->sync_id)
-			return TRUE;
+	if (hash->header->indexid != 0) {
+		/* index was just rebuilt. we should have noticed
+		   this before at index->set_lock() though. */
+		index_set_error(hash->index, "Warning: Inconsistency - Index "
+				"%s was rebuilt while we had it open",
+				hash->filepath);
+		hash->index->inconsistent = TRUE;
+		return FALSE;
 	}
 
+	mail_hash_file_close(hash);
+	if (!mail_hash_file_open(hash))
+		return FALSE;
+
 	return mmap_update_real(hash) && hash_verify_header(hash);
 
 }
@@ -143,8 +179,6 @@
 	hash->fd = -1;
 	hash->index = index;
 	hash->filepath = i_strconcat(index->filepath, ".hash", NULL);
-	hash->dirty_mmap = TRUE;
-
 	index->hash = hash;
 	return hash;
 }
@@ -163,9 +197,8 @@
 
 	hash = mail_hash_new(index);
 
-	hash->fd = open(hash->filepath, O_RDWR);
-	if (hash->fd == -1)
-		return mail_hash_rebuild(hash);
+	if (!mail_hash_file_open(hash))
+		return FALSE;
 
 	if (!mmap_update_real(hash)) {
 		/* mmap() failure is fatal */
@@ -174,11 +207,11 @@
 	}
 
 	/* make sure the header looks fine */
-	failed = TRUE;
-	if (hash_verify_header(hash)) {
-		if (hash->header->indexid == hash->index->indexid)
-			failed = FALSE;
-		else {
+	if (!hash_verify_header(hash))
+		failed = TRUE;
+	else {
+		failed = hash->header->indexid != hash->index->indexid;
+		if (failed) {
 			index_set_error(hash->index,
 					"IndexID mismatch for hash file %s",
 					hash->filepath);
@@ -188,7 +221,6 @@
 	if (failed) {
 		/* recreate it */
 		hash_munmap(hash);
-		hash->dirty_mmap = TRUE;
 
 		return mail_hash_rebuild(hash);
 	}
@@ -255,7 +287,6 @@
 	/* setup header */
 	hdr = mmap_base;
 	hdr->indexid = index->indexid;
-	hdr->sync_id = ioloop_time;
 	hdr->used_records = count;
 }
 
@@ -304,6 +335,28 @@
 	return !failed;
 }
 
+/* set indexid to 0 in hash file */
+static int mail_hash_mark_deleted(MailHash *hash)
+{
+	MailIndexDataHeader hdr;
+
+	if (hash->fd == -1) {
+		/* see if we can open it */
+		hash->fd = open(hash->filepath, O_RDWR);
+		if (hash->fd == -1)
+			return TRUE;
+	}
+
+	memset(&hdr, 0, sizeof(hdr));
+	if (write_full(hash->fd, &hdr, sizeof(hdr)) < 0) {
+		index_file_set_syscall_error(hash->index, hash->filepath,
+					     "write_full()");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 int mail_hash_rebuild(MailHash *hash)
 {
 	MailIndexHeader *index_header;
@@ -338,6 +391,9 @@
 		failed = !hash_rebuild_to_file(hash->index, fd,
 					       path, hash_size);
 
+		if (!failed)
+			failed = !mail_hash_mark_deleted(hash);
+
 		if (!failed && rename(path, hash->filepath) < 0) {
 			index_set_error(hash->index, "rename(%s, %s) failed: "
 					"%m", path, hash->filepath);
@@ -377,7 +433,6 @@
 		(void)close(hash->fd);
 	hash->fd = fd;
 	hash->anon_mmap = fd == -1;
-	hash->dirty_mmap = !hash->anon_mmap;
 	return TRUE;
 }
 
--- a/src/lib-index/mail-hash.h	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-index/mail-hash.h	Sun Sep 15 09:30:29 2002 +0300
@@ -6,10 +6,7 @@
 
 struct _MailHashHeader {
 	unsigned int indexid;
-	unsigned int sync_id;
 	unsigned int used_records;
-
-	unsigned int alignment;
 };
 
 struct _MailHashRecord {
--- a/src/lib-index/mail-index-compress.c	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-index/mail-index-compress.c	Sun Sep 15 09:30:29 2002 +0300
@@ -184,7 +184,11 @@
 		index_file_set_syscall_error(index, temppath, "close()");
 
 	if (!failed) {
-		/* now, rename the temp file to new data file */
+		/* now, rename the temp file to new data file. but before that
+		   reset indexid to make sure that other processes know the
+		   data file is closed. */
+		(void)mail_index_data_mark_deleted(index->data);
+
 		mail_index_data_free(index->data);
 
 		datapath = t_strconcat(index->filepath, DATA_FILE_PREFIX, NULL);
--- a/src/lib-index/mail-index-data.c	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-index/mail-index-data.c	Sun Sep 15 09:30:29 2002 +0300
@@ -69,10 +69,45 @@
 	return FALSE;
 }
 
+static int data_file_reopen(MailIndexData *data)
+{
+	int fd;
+
+	fd = open(data->filepath, O_RDWR);
+	if (fd == -1)
+		return index_data_set_syscall_error(data, "open()");
+
+	if (close(data->fd) < 0)
+		index_data_set_syscall_error(data, "close()");
+
+	data->fd = fd;
+	return TRUE;
+}
+
 static int mmap_update(MailIndexData *data, uoff_t pos, size_t size)
 {
 	MailIndexDataHeader *hdr;
 
+	if (data->header != NULL &&
+	    data->header->indexid != data->index->indexid) {
+		if (data->header->indexid != 0) {
+			/* index was just rebuilt. we should have noticed
+			   this before at index->set_lock() though. */
+			index_set_error(data->index,
+					"Warning: Inconsistency - Index "
+					"%s was rebuilt while we had it open",
+					data->filepath);
+			data->index->inconsistent = TRUE;
+			return FALSE;
+		}
+
+		/* data file was deleted, reopen it */
+		if (!data_file_reopen(data))
+			return FALSE;
+
+		size = 0;
+	}
+
 	if (size != 0) {
 		if (pos + size <= data->mmap_used_length)
 			return TRUE;
@@ -132,8 +167,7 @@
 			/* doesn't exist, rebuild the index */
 			INDEX_MARK_CORRUPTED(index);
 		}
-		index_set_error(index, "Can't open index data %s: %m", path);
-		return FALSE;
+		return index_file_set_syscall_error(index, path, "open()");
 	}
 
 	data = i_new(MailIndexData, 1);
@@ -301,6 +335,18 @@
 	return TRUE;
 }
 
+int mail_index_data_mark_deleted(MailIndexData *data)
+{
+	if (data->anon_mmap)
+		return TRUE;
+
+	data->header->indexid = 0;
+	if (msync(data->mmap_base, 0, sizeof(MailIndexDataHeader)) < 0)
+		return index_data_set_syscall_error(data, "msync()");
+
+	return TRUE;
+}
+
 static int mail_index_data_grow(MailIndexData *data, size_t size)
 {
 	void *base;
--- a/src/lib-index/mail-index-data.h	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-index/mail-index-data.h	Sun Sep 15 09:30:29 2002 +0300
@@ -10,6 +10,10 @@
 /* Truncate the data file and update it's indexid */
 int mail_index_data_reset(MailIndexData *data);
 
+/* Set indexid to 0 to notify other processes using this file that they should
+   re-open it. */
+int mail_index_data_mark_deleted(MailIndexData *data);
+
 /* Append new data at the end of the file. Returns the position in file
    where the data begins, or 0 if error occured. */
 uoff_t mail_index_data_append(MailIndexData *data, const void *buffer,
--- a/src/lib-index/mail-index.c	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-index/mail-index.c	Sun Sep 15 09:30:29 2002 +0300
@@ -999,7 +999,7 @@
 
 int mail_index_is_diskspace_error(MailIndex *index)
 {
-	return index->nodiskspace;
+	return !index->inconsistent && index->nodiskspace;
 }
 
 int mail_index_is_inconsistency_error(MailIndex *index)
--- a/src/lib-index/mail-lockdir.c	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-index/mail-lockdir.c	Sun Sep 15 09:30:29 2002 +0300
@@ -51,23 +51,19 @@
 	return FALSE;
 }
 
-static int mail_index_unlock_dir(MailIndex *index, const char *path,
+static int mail_index_unlock_dir(MailIndex *index, const char *private_path,
 				 const char *lockpath)
 {
 	struct stat st, lockst;
 
-	if (stat(lockpath, &st) != 0) {
-		index_set_error(index, "stat() failed for lock file %s: %m",
-				lockpath);
-		return FALSE;
-	}
+	if (stat(lockpath, &st) < 0)
+		return index_file_set_syscall_error(index, lockpath, "stat()");
 
 	if (st.st_nlink > 1) {
 		/* make sure we're really the one who's locked it */
-		if (stat(path, &lockst) != 0) {
-			index_set_error(index, "stat() failed for lock file "
-					"%s: %m", path);
-			return FALSE;
+		if (stat(private_path, &lockst) < 0) {
+			return index_file_set_syscall_error(index, private_path,
+							    "stat()");
 		}
 
 		if (st.st_dev != lockst.st_dev ||
@@ -84,20 +80,22 @@
 	}
 
 	/* first unlink the actual lock file */
-	if (unlink(lockpath) == -1) {
-		index_set_error(index, "unlink() failed for lock file %s: %m",
-				lockpath);
+	if (unlink(lockpath) < 0) {
+		index_file_set_syscall_error(index, lockpath, "unlink()");
 		return FALSE;
 	}
 
-	(void)unlink(path);
+	if (unlink(private_path) < 0) {
+		/* non-fatal */
+		index_file_set_syscall_error(index, private_path, "unlink()");
+	}
 	return TRUE;
 }
 
 int mail_index_lock_dir(MailIndex *index, MailLockType lock_type)
 {
 	struct stat st;
-	const char *path, *lockpath;
+	const char *private_path, *lockpath;
 	int fd, orig_errno, first;
 	time_t max_wait_time;
 
@@ -108,33 +106,39 @@
 
 	/* use .dirlock.host.pid as our lock indicator file and
 	   .dirlock as the real lock */
-	path = t_strconcat(index->dir, "/" DIRLOCK_FILE_PREFIX ".",
-			   my_hostname, ".", my_pid, NULL);
+	private_path = t_strconcat(index->dir, "/" DIRLOCK_FILE_PREFIX ".",
+				   my_hostname, ".", my_pid, NULL);
 	lockpath = t_strconcat(index->dir, "/" DIRLOCK_FILE_PREFIX, NULL);
 
 	if (lock_type == MAIL_LOCK_UNLOCK)
-		return mail_index_unlock_dir(index, path, lockpath);
+		return mail_index_unlock_dir(index, private_path, lockpath);
 
-	(void)unlink(path);
-	fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0660);
+	(void)unlink(private_path);
+	fd = open(private_path, O_RDWR | O_CREAT | O_EXCL, 0660);
 	if (fd == -1) {
-		index_set_error(index, "Can't create lock file %s: %m", path);
+		if (errno == ENOSPC)
+			index->nodiskspace = TRUE;
+		index_file_set_syscall_error(index, private_path, "open()");
 		return FALSE;
 	}
 
 	/* try to link the file into lock file. */
 	first = TRUE; max_wait_time = time(NULL) + MAX_LOCK_WAIT_SECONDS;
-	while (link(path, lockpath) == -1) {
+	while (link(private_path, lockpath) < 0) {
+		if (errno == ENOSPC)
+			index->nodiskspace = TRUE;
+
 		if (errno != EEXIST) {
 			orig_errno = errno;
 
 			/* NFS may die and link() fail even if it really
 			   was created */
-			if (stat(path, &st) == 0 && st.st_nlink == 2)
+			if (stat(private_path, &st) == 0 && st.st_nlink == 2)
 				break;
 
+			errno = orig_errno;
 			index_set_error(index, "link(%s, %s) lock failed: %m",
-					path, lockpath);
+					private_path, lockpath);
 			return FALSE;
 		}
 
--- a/src/lib-index/mail-modifylog.c	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-index/mail-modifylog.c	Sun Sep 15 09:30:29 2002 +0300
@@ -31,22 +31,22 @@
 	unsigned int synced_id, mmaped_id;
 
 	unsigned int modified:1;
-	unsigned int dirty_mmap:1;
 	unsigned int second_log:1;
 };
 
 static const unsigned int no_expunges[] = { 0 };
 
-static void modifylog_set_syscall_error(MailModifyLog *log,
+static int modifylog_set_syscall_error(MailModifyLog *log,
 					const char *function)
 {
 	i_assert(function != NULL);
 
 	index_set_error(log->index, "%s failed with modify log file %s: %m",
 			function, log->filepath);
+	return FALSE;
 }
 
-static void modifylog_set_corrupted(MailModifyLog *log)
+static int modifylog_set_corrupted(MailModifyLog *log)
 {
 	index_set_error(log->index, "Modify log %s is corrupted",
 			log->filepath);
@@ -54,18 +54,8 @@
 	/* make sure we don't get back here */
 	log->index->inconsistent = TRUE;
 	(void)unlink(log->filepath);
-}
 
-/* Returns 1 = ok, 0 = failed to get the lock, -1 = error */
-static int mail_modifylog_try_lock(MailModifyLog *log, int lock_type)
-{
-	int ret;
-
-	ret = file_try_lock(log->fd, lock_type);
-	if (ret == -1)
-                modifylog_set_syscall_error(log, "file_try_lock()");
-
-	return ret;
+	return FALSE;
 }
 
 static int mail_modifylog_wait_lock(MailModifyLog *log)
@@ -82,46 +72,55 @@
 	int ret;
 
 	/* try grabbing exclusive lock */
-	ret = mail_modifylog_try_lock(log, F_WRLCK);
-	if (ret == -1)
-		return -1;
+	ret = file_try_lock(log->fd, F_WRLCK);
+	if (ret <= 0) {
+		if (ret < 0)
+			modifylog_set_syscall_error(log, "file_try_lock()");
+		return ret;
+	}
 
 	/* revert back to shared lock */
-	switch (mail_modifylog_try_lock(log, F_RDLCK)) {
-	case 0:
+	ret = file_try_lock(log->fd, F_RDLCK);
+	if (ret < 0) {
+		modifylog_set_syscall_error(log, "file_try_lock()");
+		return -1;
+	}
+
+	if (ret == 0) {
 		/* shouldn't happen */
 		index_set_error(log->index, "file_lock(F_WRLCK -> F_RDLCK) "
 				"failed with file %s", log->filepath);
-		/* fall through */
-	case -1:
 		return -1;
 	}
 
-	return ret == 0 ? 1 : 0;
+	return 1;
 }
 
 static int mmap_update(MailModifyLog *log)
 {
 	unsigned int extra;
 
-	if (!log->dirty_mmap && log->mmaped_id == log->header->sync_id)
+	if (log->header != NULL && log->mmaped_id == log->header->sync_id)
 		return TRUE;
 
-	if (log->mmap_base != NULL)
-		(void)munmap(log->mmap_base, log->mmap_length);
+	if (log->mmap_base != NULL) {
+		if (munmap(log->mmap_base, log->mmap_length) < 0)
+			modifylog_set_syscall_error(log, "munmap()");
+	}
+
+	log->header = NULL;
 
 	log->mmap_base = mmap_rw_file(log->fd, &log->mmap_length);
 	if (log->mmap_base == MAP_FAILED) {
 		log->mmap_base = NULL;
-		log->header = NULL;
-		modifylog_set_syscall_error(log, "mmap()");
-		return FALSE;
+		return modifylog_set_syscall_error(log, "mmap()");
 	}
 
 	if (log->mmap_length < sizeof(ModifyLogHeader)) {
-		/* FIXME: we could do better.. */
+		index_set_error(log->index, "Too small modify log %s",
+				log->filepath);
 		(void)unlink(log->filepath);
-		i_panic("modify log corrupted");
+		return FALSE;
 	}
 
 	extra = (log->mmap_length - sizeof(ModifyLogHeader)) %
@@ -131,10 +130,10 @@
 		/* partial write or corrupted -
 		   truncate the file to valid length */
 		log->mmap_length -= extra;
-		(void)ftruncate(log->fd, (off_t)log->mmap_length);
+		if (ftruncate(log->fd, (off_t)log->mmap_length) < 0)
+			modifylog_set_syscall_error(log, "ftruncate()");
 	}
 
-	log->dirty_mmap = FALSE;
 	log->header = log->mmap_base;
 	log->mmaped_id = log->header->sync_id;
 	return TRUE;
@@ -147,7 +146,6 @@
 	log = i_new(MailModifyLog, 1);
 	log->fd = -1;
 	log->index = index;
-	log->dirty_mmap = TRUE;
 
 	index->modifylog = log;
 	return log;
@@ -155,8 +153,6 @@
 
 static void mail_modifylog_close(MailModifyLog *log)
 {
-	log->dirty_mmap = TRUE;
-
 	if (log->mmap_base != NULL) {
 		munmap(log->mmap_base, log->mmap_length);
 		log->mmap_base = NULL;
@@ -180,14 +176,12 @@
 	hdr.indexid = log->index->indexid;
 
 	if (write_full(fd, &hdr, sizeof(hdr)) < 0) {
-		index_set_error(log->index, "write() failed for modify "
-				"log %s: %m", path);
+		index_file_set_syscall_error(log->index, path, "write_full()");
 		return FALSE;
 	}
 
 	if (ftruncate(fd, sizeof(hdr)) < 0) {
-		index_set_error(log->index, "ftruncate() failed for modify "
-				"log %s: %m", path);
+		index_file_set_syscall_error(log->index, path, "ftruncate()");
 		return FALSE;
 	}
 
@@ -201,15 +195,17 @@
 
 	fd = open(path, O_RDWR | O_CREAT, 0660);
 	if (fd == -1) {
-		index_set_error(log->index, "Error opening modify log "
-				"file %s: %m", path);
+		if (errno == ENOSPC)
+			log->index->nodiskspace = TRUE;
+
+		index_file_set_syscall_error(log->index, path, "open()");
 		return FALSE;
 	}
 
 	ret = file_wait_lock(fd, F_WRLCK);
 	if (ret == -1) {
-		index_set_error(log->index, "Error locking modify log "
-				"file %s: %m", path);
+		index_file_set_syscall_error(log->index, path,
+					     "file_wait_lock()");
 	}
 
 	if (ret == 1 && mail_modifylog_init_fd(log, fd, path)) {
@@ -256,16 +252,15 @@
 	fd = open(path, O_RDWR);
 	if (fd == -1) {
 		if (errno != ENOENT) {
-			index_set_error(log->index, "Can't open modify log "
-					"file %s: %m", path);
+			index_file_set_syscall_error(log->index, path,
+						     "open()");
 		}
 		return -1;
 	}
 
 	ret = 1;
 	if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
-		index_set_error(log->index, "read() failed when for modify "
-				"log file %s: %m", path);
+		index_file_set_syscall_error(log->index, path, "read()");
 		ret = -1;
 	}
 
@@ -361,16 +356,12 @@
 		return TRUE;
 
 	if (log->mmap_base != NULL) {
-		if (msync(log->mmap_base, log->mmap_length, MS_SYNC) < 0) {
-			modifylog_set_syscall_error(log, "msync()");
-			return FALSE;
-		}
+		if (msync(log->mmap_base, log->mmap_length, MS_SYNC) < 0)
+			return modifylog_set_syscall_error(log, "msync()");
 	}
 
-	if (fsync(log->fd) < 0) {
-		modifylog_set_syscall_error(log, "fsync()");
-		return FALSE;
-	}
+	if (fsync(log->fd) < 0)
+		return modifylog_set_syscall_error(log, "fsync()");
 
 	log->modified = FALSE;
 	return TRUE;
@@ -394,24 +385,20 @@
 		}
 	}
 
-	if (lseek(log->fd, 0, SEEK_END) < 0) {
-		modifylog_set_syscall_error(log, "lseek()");
-		return FALSE;
-	}
+	if (lseek(log->fd, 0, SEEK_END) < 0)
+		return modifylog_set_syscall_error(log, "lseek()");
 
-	if (write_full(log->fd, rec, sizeof(ModifyLogRecord)) < 0) {
-                modifylog_set_syscall_error(log, "write_full()");
-		return FALSE;
+	if (write_full(log->fd, rec, sizeof(ModifyLogRecord)) < 0)
+                return modifylog_set_syscall_error(log, "write_full()");
+
+	if (!external_change && log->header->sync_id == log->synced_id) {
+		log->synced_position += sizeof(ModifyLogRecord);
+		log->synced_id++;
 	}
 
 	log->header->sync_id++;
 	log->modified = TRUE;
-	log->dirty_mmap = TRUE;
 
-	if (!external_change) {
-		log->synced_id = log->header->sync_id;
-		log->synced_position += sizeof(ModifyLogRecord);
-	}
 	return TRUE;
 }
 
--- a/src/lib-storage/index/index-copy.c	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-storage/index/index-copy.c	Sun Sep 15 09:30:29 2002 +0300
@@ -11,12 +11,12 @@
 typedef struct {
 	Mailbox *dest;
 	const char **custom_flags;
-} CopyData;
+} CopyContext;
 
 static int copy_func(MailIndex *index, MailIndexRecord *rec,
 		     unsigned int seq __attr_unused__, void *context)
 {
-	CopyData *cd = context;
+	CopyContext *ctx = context;
 	IOBuffer *inbuf;
 	int failed;
 
@@ -25,9 +25,10 @@
 		return FALSE;
 
 	/* save it in destination mailbox */
-	failed = !cd->dest->save(cd->dest, rec->msg_flags,
-				 cd->custom_flags, rec->internal_date,
-				 inbuf, inbuf->size);
+	failed = !ctx->dest->save(ctx->dest, rec->msg_flags,
+				  ctx->custom_flags, rec->internal_date,
+				  inbuf, inbuf->size);
+
 	(void)close(inbuf->fd);
 	io_buffer_destroy(inbuf);
 	return !failed;
@@ -37,7 +38,7 @@
 		       const char *messageset, int uidset)
 {
 	IndexMailbox *ibox = (IndexMailbox *) box;
-        CopyData cd;
+        CopyContext ctx;
 	int failed;
 
 	if (destbox->readonly) {
@@ -52,11 +53,12 @@
 	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_SHARED))
 		return mail_storage_set_index_error(ibox);
 
-	cd.custom_flags = mail_custom_flags_list_get(ibox->index->custom_flags);
-	cd.dest = destbox;
+	ctx.custom_flags =
+		mail_custom_flags_list_get(ibox->index->custom_flags);
+	ctx.dest = destbox;
 
 	failed = index_messageset_foreach(ibox, messageset, uidset,
-					  copy_func, &cd) <= 0;
+					  copy_func, &ctx) <= 0;
 
 	mail_custom_flags_list_unref(ibox->index->custom_flags);
 
--- a/src/lib-storage/index/index-fetch.c	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-storage/index/index-fetch.c	Sun Sep 15 09:30:29 2002 +0300
@@ -326,8 +326,8 @@
 	MailFetchBodyData *sect;
 	int ret;
 
-	if (!ibox->index->sync(ibox->index))
-		return mail_storage_set_index_error(ibox);
+	if (!index_storage_sync_if_possible(ibox))
+		return FALSE;
 
 	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_SHARED))
 		return mail_storage_set_index_error(ibox);
--- a/src/lib-storage/index/index-save.c	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-storage/index/index-save.c	Sun Sep 15 09:30:29 2002 +0300
@@ -34,7 +34,7 @@
 		}
 	}
 
-	return write_full(fd, data, size) < 0;
+	return write_full(fd, data, size) >= 0;
 }
 
 int index_storage_save_into_fd(MailStorage *storage, int fd, const char *path,
@@ -60,9 +60,15 @@
 			size = (size_t)data_size;
 		data_size -= size;
 
-		if (write_with_crlf(fd, data, size, &last_cr) != 0) {
-			mail_storage_set_critical(storage, "write() failed "
-						  "for file %s: %m", path);
+		if (!write_with_crlf(fd, data, size, &last_cr)) {
+			if (errno == ENOSPC) {
+				mail_storage_set_error(storage,
+						       "Not enough disk space");
+			} else {
+				mail_storage_set_critical(storage,
+							  "write() failed for "
+							  "file %s: %m", path);
+			}
 			return FALSE;
 		}
 
--- a/src/lib-storage/index/index-search.c	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-storage/index/index-search.c	Sun Sep 15 09:30:29 2002 +0300
@@ -703,8 +703,8 @@
 {
 	IndexMailbox *ibox = (IndexMailbox *) box;
 
-	if (!ibox->index->sync(ibox->index))
-		return mail_storage_set_index_error(ibox);
+	if (!index_storage_sync_if_possible(ibox))
+		return FALSE;
 
 	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_SHARED))
 		return mail_storage_set_index_error(ibox);
--- a/src/lib-storage/index/index-status.c	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-storage/index/index-status.c	Sun Sep 15 09:30:29 2002 +0300
@@ -101,8 +101,8 @@
 
 	memset(status, 0, sizeof(MailboxStatus));
 
-	if (!ibox->index->sync(ibox->index))
-		return mail_storage_set_index_error(ibox);
+	if (!index_storage_sync_if_possible(ibox))
+		return FALSE;
 
 	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_SHARED))
 		return mail_storage_set_index_error(ibox);
--- a/src/lib-storage/index/index-storage.h	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-storage/index/index-storage.h	Sun Sep 15 09:30:29 2002 +0300
@@ -22,12 +22,14 @@
 
 extern ImapMessageCacheIface index_msgcache_iface;
 
+int mail_storage_set_index_error(IndexMailbox *ibox);
+
 IndexMailbox *index_storage_init(MailStorage *storage, Mailbox *box,
 				 MailIndex *index, const char *name,
 				 int readonly);
 void index_storage_close(Mailbox *box);
 
-int mail_storage_set_index_error(IndexMailbox *ibox);
+int index_storage_sync_if_possible(IndexMailbox *ibox);
 
 int index_mailbox_fix_custom_flags(IndexMailbox *ibox, MailFlags *flags,
                                    const char *custom_flags[]);
--- a/src/lib-storage/index/index-sync.c	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-storage/index/index-sync.c	Sun Sep 15 09:30:29 2002 +0300
@@ -2,9 +2,29 @@
 
 #include "lib.h"
 #include "index-storage.h"
+#include "mail-index-util.h"
 #include "mail-modifylog.h"
 #include "mail-custom-flags.h"
 
+/* may leave the index locked */
+int index_storage_sync_if_possible(IndexMailbox *ibox)
+{
+	if (!ibox->index->sync(ibox->index)) {
+		if (!ibox->index->is_diskspace_error(ibox->index)) {
+			(void)ibox->index->set_lock(ibox->index,
+						    MAIL_LOCK_UNLOCK);
+			return mail_storage_set_index_error(ibox);
+		}
+
+		/* not enough disk space to sync. can't do much about it
+		   though, giving error message would just make it impossible
+		   to delete messages. */
+		index_reset_error(ibox->index);
+	}
+
+	return TRUE;
+}
+
 int index_storage_sync(Mailbox *box, unsigned int *messages, int expunge,
 		       MailExpungeFunc expunge_func,
 		       MailFlagUpdateFunc flag_func,
@@ -25,8 +45,8 @@
 
 	*messages = 0;
 
-	if (!ibox->index->sync(ibox->index))
-		return mail_storage_set_index_error(ibox);
+	if (!index_storage_sync_if_possible(ibox))
+		return FALSE;
 
 	if (!ibox->index->set_lock(ibox->index, expunge ?
 				   MAIL_LOCK_EXCLUSIVE : MAIL_LOCK_SHARED))
--- a/src/lib-storage/index/maildir/maildir-save.c	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-storage/index/maildir/maildir-save.c	Sun Sep 15 09:30:29 2002 +0300
@@ -26,10 +26,15 @@
 	path = t_strconcat(dir, "/", *fname, NULL);
 	fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0660);
 	if (fd == -1) {
-		/* don't bother checking if it was because file existed -
-		   if that happens it's itself an error. */
-		mail_storage_set_critical(storage,
-					  "Can't create file %s: %m", path);
+		if (errno == ENOSPC) {
+			mail_storage_set_error(storage,
+					       "Not enough disk space");
+		} else {
+			/* don't bother checking if it was because file
+			   existed - if that happens it's itself an error. */
+			mail_storage_set_critical(storage, "Can't create file "
+						  "%s: %m", path);
+		}
 	}
 
 	return fd;
@@ -97,8 +102,15 @@
 	if (rename(tmp_path, new_path) == 0)
 		failed = FALSE;
 	else {
-		mail_storage_set_critical(box->storage, "rename(%s, %s) "
-					  "failed: %m", tmp_path, new_path);
+		if (errno == ENOSPC) {
+			mail_storage_set_error(box->storage,
+					       "Not enough disk space");
+		} else {
+			mail_storage_set_critical(box->storage,
+						  "rename(%s, %s) failed: %m",
+						  tmp_path, new_path);
+		}
+
 		(void)unlink(tmp_path);
 		failed = TRUE;
 	}
--- a/src/lib-storage/index/mbox/mbox-save.c	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib-storage/index/mbox/mbox-save.c	Sun Sep 15 09:30:29 2002 +0300
@@ -15,7 +15,45 @@
 
 static char my_hostdomain[256] = "";
 
-static int write_from_line(MailStorage *storage, int fd, time_t internal_date)
+static void set_error(MailStorage *storage, const char *mbox_path)
+{
+	if (errno == ENOSPC)
+		mail_storage_set_error(storage, "Not enough disk space");
+	else {
+		mail_storage_set_critical(storage, "Error writing to "
+					  "mbox file %s: %m", mbox_path);
+	}
+}
+
+static int mbox_check_ending_lf(MailStorage *storage, int fd, off_t pos,
+				const char *mbox_path)
+{
+	char ch;
+
+	if (pos == 0)
+		return TRUE;
+
+	do {
+		if (lseek(fd, 0, pos-1) < 0)
+			break;
+
+		if (read(fd, &ch, 1) != 1)
+			break;
+
+		if (ch != '\n') {
+			if (write_full(fd, &ch, 1) < 0)
+				break;
+		}
+
+		return TRUE;
+	} while (0);
+
+	set_error(storage, mbox_path);
+	return FALSE;
+}
+
+static int write_from_line(MailStorage *storage, int fd, const char *mbox_path,
+			   time_t internal_date)
 {
 	const char *sender, *line, *name;
 	size_t len;
@@ -41,7 +79,12 @@
 	line = mbox_from_create(sender, internal_date);
 	len = strlen(line);
 
-	return write_full(fd, line, len) < 0;
+	if (write_full(fd, line, len) < 0) {
+		set_error(storage, mbox_path);
+		return FALSE;
+	}
+
+	return TRUE;
 }
 
 int mbox_storage_save(Mailbox *box, MailFlags flags, const char *custom_flags[],
@@ -49,6 +92,7 @@
 {
 	IndexMailbox *ibox = (IndexMailbox *) box;
 	off_t pos;
+	const char *mbox_path;
 	int fd, failed;
 
 	if (box->readonly) {
@@ -75,39 +119,19 @@
 	failed = FALSE;
 
 	pos = lseek(fd, 0, SEEK_END);
-	if (pos == -1) {
+	if (pos < 0) {
 		mail_storage_set_critical(box->storage,
 					  "lseek() failed for mbox file %s: %m",
 					  ibox->index->mbox_path);
 		failed = TRUE;
 	} else {
-		if (pos > 0) {
-			/* make sure the file ends with \n */
-			if (lseek(fd, 0, pos-1) != pos-1)
-				failed = TRUE;
-			else {
-				char ch;
+		mbox_path = ibox->index->mbox_path;
 
-				if (read(fd, &ch, 1) != 1)
-					failed = TRUE;
-				else if (ch != '\n') {
-					if (write_full(fd, &ch, 1) < 0)
-						failed = TRUE;
-				}
-			}
-		}
-
-		if (failed) {
-			/* don't bother separating the errors, it's very
-			   unlikely that this will happen */
-			mail_storage_set_critical(box->storage,
-						  "Error appending LF to mbox "
-						  "file %s: %m",
-						  ibox->index->mbox_path);
-		} else if (!write_from_line(box->storage, fd, internal_date) ||
-			   !index_storage_save_into_fd(box->storage, fd,
-						       ibox->index->mbox_path,
-						       data, data_size)) {
+		if (!mbox_check_ending_lf(box->storage, fd, pos, mbox_path) ||
+		    !write_from_line(box->storage, fd, mbox_path,
+				     internal_date) ||
+		    !index_storage_save_into_fd(box->storage, fd, mbox_path,
+						data, data_size)) {
 			/* failed, truncate file back to original size */
 			(void)ftruncate(fd, pos);
 			failed = TRUE;
--- a/src/lib/write-full.h	Sat Sep 14 14:09:42 2002 +0300
+++ b/src/lib/write-full.h	Sun Sep 15 09:30:29 2002 +0300
@@ -2,7 +2,7 @@
 #define __WRITE_FULL_H
 
 /* Write data into file. Returns -1 if error occured, or 0 if all was ok.
-   If there's not enough space in device -1 with ENOSPC is returned, and
+   If there's not enough space in device, -1 with ENOSPC is returned, and
    it's unspecified how much data was actually written. */
 int write_full(int fd, const void *data, size_t size);