changeset 29:e9375147c0cb HEAD

Added write_full() which is a simple wrapper around write() meant for writing into files. When there's too much deleted data in index files, they're now compressed when the index is being opened.
author Timo Sirainen <tss@iki.fi>
date Mon, 26 Aug 2002 02:46:59 +0300
parents 6ac07b3fe0ff
children 8509281fda72
files TODO src/lib-index/Makefile.am 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-index.h src/lib-index/mail-modifylog.c src/lib-storage/flags-file/flags-file.c src/lib-storage/index/index-save.c src/lib-storage/index/mbox/mbox-save.c src/lib-storage/subscription-file/subscription-file.c src/lib/Makefile.am src/lib/write-full.c src/lib/write-full.h
diffstat 17 files changed, 318 insertions(+), 68 deletions(-) [+]
line wrap: on
line diff
--- a/TODO	Mon Aug 26 02:22:41 2002 +0300
+++ b/TODO	Mon Aug 26 02:46:59 2002 +0300
@@ -25,27 +25,17 @@
        the first UID in index.
  - mbox:
      - BUG: adding new mail after indexes are created doesn't work
-     - save MD5 sums for messages?
-     - update Status and X-Status headers when flags are changed
-     - last \n shouldn't be sent for messages. also remember to fix
-       the From-checks to check for [\r]\nFrom instead then..
+     - update Status and X-Status headers when flags are changed.
+       how? probably by just writing a new mbox file at close time.
      - EXPUNGE doesn't delete the mail from the mbox file
-     - fsck should probably (or optionally?) really scan the message body
-       for "\nFrom " text instead of just jumping over the message body.
-       Quite useless actually, but this would make it fully reliable with md5
-       anyway..
  - there's some race condition issues when opening mailboxes..
- - when opening index files, check the flags and do what's needed. fsck and
-   rebuild is supported currently. compression and hash rebuilding is still
-   needed. and the cache_fields .. not sure when that'd be done, preferably
-   in the separate compress-process..
  - set_lock() is ugly and horrible and should really be done something.
    does the syncing really need to be there? maybe put it into separate
    function which can be called after set_lock() by functions which actually
    care about the sync state (fetch, search, store, etc).
  - read-only support for mailboxes where we don't have write-access? Could be
-   a bigger job. At least the mmap()ed file contents can't be trusted since
-   they might change at any time.
+   a bigger job. At least the mmap()ed file contents (== pretty much
+   everything) can't be trusted since they might change at any time.
  - if index was just rebuilt, modify log complains about indexid mismatch
    at first open
  - does append work?
@@ -65,7 +55,8 @@
  - maildir: atomic COPY could be done by setting a "temporary" flag into the
    file's name. once copying is done, set an ignore-temporary field into
    index's header. at next sync the temporary flag will be removed.
- - mbox: internal_date isn't saved 
+ - mbox: should we bother checking if mbox file doesn't end with \n when 
+   appending?
  - select "" shouldn't work.
 
 general:
@@ -108,7 +99,13 @@
  - Something's wrong with expunging mails from maildir ..
  - PAM: support some options so /etc/passwd-lookup isn't needed. uid=x, gid=y,
    mailroot=/var/mail. maildirs should be then created when needed
- - check for "../" folder names with mbox
+ - index_rec->full_virtual_size could be 0 to indicate it's not calculated
+   yet. this way we don't need to read the messages fully just to find out
+   that.
+ - ability to automatically try again if some command fails because error
+   occured in the middle of it, but was most likely also fixed. for example
+   if mbox is compressed in the middle of FETCH operation or some index
+   corruption was noticed
 
 cleanups / checks:
  - grep for FIXME
@@ -121,7 +118,7 @@
  - allocating readwrite pools now just uses system_pool .. so pool_unref()
    can't free memory used by it .. what to do about it? at least count the
    malloc/free calls and make complain if at the exit they don't match
- - Make sure messages of size INT_MAX..UINT_MAX (and more) work correctly
+ - Make sure messages of size INT_MAX..UINT_MAX (and more) work correctly.
    virtual_size can also overflow making it less than physical_size
 
 optional optimizations:
--- a/src/lib-index/Makefile.am	Mon Aug 26 02:22:41 2002 +0300
+++ b/src/lib-index/Makefile.am	Mon Aug 26 02:46:59 2002 +0300
@@ -10,6 +10,7 @@
 libstorage_index_a_SOURCES = \
 	mail-hash.c \
         mail-index.c \
+        mail-index-compress.c \
         mail-index-fsck.c \
         mail-index-data.c \
         mail-index-update.c \
--- a/src/lib-index/mail-hash.c	Mon Aug 26 02:22:41 2002 +0300
+++ b/src/lib-index/mail-hash.c	Mon Aug 26 02:46:59 2002 +0300
@@ -3,6 +3,7 @@
 #include "lib.h"
 #include "primes.h"
 #include "mmap-util.h"
+#include "write-full.h"
 #include "mail-index.h"
 #include "mail-index-util.h"
 #include "mail-hash.h"
@@ -172,13 +173,13 @@
 	/* write in 1kb blocks */
 	full_blocks = size / sizeof(block);
 	for (i = 0; i < full_blocks; i++) {
-		if (write(fd, block, sizeof(block)) != sizeof(block))
+		if (write_full(fd, block, sizeof(block)) < 0)
 			return FALSE;
 	}
 
 	/* write the remainder */
 	i = size % sizeof(block);
-	return i == 0 ? TRUE : (size_t) write(fd, block, i) == i;
+	return i == 0 ? TRUE : write_full(fd, block, i) == 0;
 }
 
 static int hash_rebuild_to_file(MailIndex *index, int fd,
--- a/src/lib-index/mail-hash.h	Mon Aug 26 02:22:41 2002 +0300
+++ b/src/lib-index/mail-hash.h	Mon Aug 26 02:46:59 2002 +0300
@@ -24,7 +24,7 @@
 /* Synchronize the hash file with memory map */
 int mail_hash_sync_file(MailHash *hash);
 
-/* Rebuild hash from index and reset the FLAG_REBUILD in header.
+/* Rebuild hash from index and reset the FLAG_REBUILD_HASH in header.
    The index must have an exclusive lock before this function is called. */
 int mail_hash_rebuild(MailHash *hash);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-index/mail-index-compress.c	Mon Aug 26 02:46:59 2002 +0300
@@ -0,0 +1,152 @@
+/* Copyright (C) 2002 Timo Sirainen */
+
+#include "lib.h"
+#include "write-full.h"
+#include "mail-index.h"
+#include "mail-index-data.h"
+#include "mail-index-util.h"
+
+#include <stdio.h>
+#include <unistd.h>
+
+int mail_index_compress(MailIndex *index)
+{
+	MailIndexHeader *hdr;
+	MailIndexRecord *rec, *hole_rec, *end_rec;
+	off_t fsize;
+
+	if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE))
+		return FALSE;
+
+	hdr = index->get_header(index);
+
+	if (hdr->first_hole_position == 0) {
+		/* we don't need to compress after all. shouldn't happen.. */
+		index->header->flags &= ~MAIL_INDEX_FLAG_CACHE_FIELDS;
+		return TRUE;
+	}
+
+	/* if we get interrupted, the whole index is probably corrupted.
+	   so keep rebuild-flag on while doing this */
+	hdr->flags |= MAIL_INDEX_FLAG_REBUILD;
+	if (!mail_index_fmsync(index, sizeof(MailIndexHeader)))
+		return FALSE;
+
+	/* first actually compress the data */
+	end_rec = (MailIndexRecord *) ((char *) index->mmap_base +
+				       index->mmap_length);
+	hole_rec = (MailIndexRecord *) ((char *) index->mmap_base +
+					hdr->first_hole_position);
+	rec = hole_rec + hdr->first_hole_records;
+	while (rec < end_rec) {
+		if (rec->uid != 0) {
+			memcpy(hole_rec, rec, sizeof(MailIndexRecord));
+			hole_rec++;
+		}
+		rec++;
+	}
+
+	/* truncate the file to get rid of the extra records */
+	fsize = (char *) hole_rec - (char *) index->mmap_base;
+	if (ftruncate(index->fd, fsize) == -1) {
+		index_set_error(index, "ftruncate() failed for %s: %m",
+				index->filepath);
+		return FALSE;
+	}
+
+	/* update headers */
+	index->header->first_hole_position = 0;
+	index->header->first_hole_records = 0;
+
+	/* make sure the whole file is synced before removing rebuild-flag */
+	if (!mail_index_fmsync(index, fsize))
+		return FALSE;
+
+	index->header->flags &= ~(MAIL_INDEX_FLAG_CACHE_FIELDS |
+				  MAIL_INDEX_FLAG_REBUILD);
+	return TRUE;
+}
+
+int mail_index_compress_data(MailIndex *index)
+{
+	MailIndexRecord *rec;
+	MailIndexDataHeader data_hdr;
+	unsigned char *mmap_data;
+	size_t mmap_data_size;
+	off_t offset;
+	const char *temppath, *datapath;
+	int fd;
+
+	/* write the data into temporary file updating the offsets in index
+	   while doing it. if we fail (especially if out of disk space/quota)
+	   we'll simply fail and index is rebuilt later */
+	if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE))
+		return FALSE;
+
+	mmap_data = mail_index_data_get_mmaped(index->data, &mmap_data_size);
+	if (mmap_data == NULL)
+		return FALSE;
+
+	fd = mail_index_create_temp_file(index, &temppath);
+	if (fd == -1)
+		return FALSE;
+
+	/* write data header */
+	memset(&data_hdr, 0, sizeof(data_hdr));
+	data_hdr.indexid = index->indexid;
+	if (write_full(fd, &data_hdr, sizeof(data_hdr)) < 0) {
+		index_set_error(index, "Error writing to temp index data "
+				"%s: %m", temppath);
+		(void)close(fd);
+		(void)unlink(temppath);
+		return FALSE;
+	}
+
+	/* no we'll begin the actual moving. keep rebuild-flag on
+	   while doing it. */
+	index->header->flags |= MAIL_INDEX_FLAG_REBUILD;
+	if (!mail_index_fmsync(index, sizeof(MailIndexHeader))) {
+		(void)close(fd);
+		(void)unlink(temppath);
+		return FALSE;
+	}
+
+	offset = sizeof(data_hdr);
+	rec = index->lookup(index, 1);
+	while (rec != NULL) {
+		if (write_full(fd, mmap_data + rec->data_position,
+			       rec->data_size) < 0) {
+			index_set_error(index, "Error writing to temp index "
+					"data %s: %m", temppath);
+			(void)close(fd);
+			(void)unlink(temppath);
+			return FALSE;
+		}
+
+		rec->data_position = offset;
+		offset += rec->data_size;
+
+		rec = index->next(index, rec);
+	}
+
+	/* now, close the old data file and rename the temp file into
+	   new data file */
+	mail_index_data_free(index->data);
+	(void)close(fd);
+
+	datapath = t_strconcat(index->filepath, DATA_FILE_PREFIX, NULL);
+	if (rename(temppath, datapath) < 0) {
+		index_set_error(index, "rename(%s, %s) failed: %m",
+				temppath, datapath);
+		return FALSE;
+	}
+
+	/* make sure the whole file is synced before removing rebuild-flag */
+	if (!mail_index_fmsync(index, index->mmap_length))
+		return FALSE;
+
+	index->header->flags &= ~(MAIL_INDEX_FLAG_CACHE_FIELDS |
+				  MAIL_INDEX_FLAG_REBUILD);
+
+	return mail_index_data_open(index);
+}
--- a/src/lib-index/mail-index-data.c	Mon Aug 26 02:22:41 2002 +0300
+++ b/src/lib-index/mail-index-data.c	Mon Aug 26 02:46:59 2002 +0300
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "mmap-util.h"
+#include "write-full.h"
 #include "mail-index.h"
 #include "mail-index-data.h"
 #include "mail-index-util.h"
@@ -62,7 +63,7 @@
 	const char *path;
 	int fd;
 
-	path = t_strconcat(index->filepath, ".data", NULL);
+	path = t_strconcat(index->filepath, DATA_FILE_PREFIX, NULL);
 	fd = open(path, O_RDWR);
 	if (fd == -1) {
 		if (errno == ENOENT) {
@@ -109,7 +110,7 @@
 	memset(&hdr, 0, sizeof(hdr));
 	hdr.indexid = index->indexid;
 
-	if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+	if (write_full(fd, &hdr, sizeof(hdr)) < 0) {
 		index_set_error(index, "Error writing to temp index data "
 				"%s: %m", temppath);
 		return NULL;
@@ -117,7 +118,7 @@
 
 	/* move temp file into .data file, deleting old one
 	   if it already exists */
-	realpath = t_strconcat(index->filepath, ".data", NULL);
+	realpath = t_strconcat(index->filepath, DATA_FILE_PREFIX, NULL);
 	if (rename(temppath, realpath) == -1) {
 		index_set_error(index, "rename(%s, %s) failed: %m",
 				temppath, realpath);
@@ -189,7 +190,7 @@
 		return FALSE;
 	}
 
-	if (write(data->fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+	if (write_full(data->fd, &hdr, sizeof(hdr)) < 0) {
 		index_set_error(data->index, "write() failed for data file "
 				"%s: %m", data->filepath);
 		return FALSE;
@@ -216,7 +217,7 @@
 		return -1;
 	}
 
-	if ((size_t) write(data->fd, buffer, size) != size) {
+	if (write_full(data->fd, buffer, size) < 0) {
 		index_set_error(data->index, "Error appending to file %s: %m",
 				data->filepath);
 		return -1;
@@ -376,3 +377,12 @@
 			(unsigned long) DATA_FILE_POSITION(data, rec));
 	return FALSE;
 }
+
+void *mail_index_data_get_mmaped(MailIndexData *data, size_t *size)
+{
+	if (!mmap_update(data, 0, UINT_MAX))
+		return NULL;
+
+	*size = data->mmap_length;
+	return data->mmap_base;
+}
--- a/src/lib-index/mail-index-data.h	Mon Aug 26 02:22:41 2002 +0300
+++ b/src/lib-index/mail-index-data.h	Mon Aug 26 02:46:59 2002 +0300
@@ -1,6 +1,8 @@
 #ifndef __MAIL_INDEX_DATA_H
 #define __MAIL_INDEX_DATA_H
 
+#define DATA_FILE_PREFIX ".data"
+
 int mail_index_data_open(MailIndex *index);
 int mail_index_data_create(MailIndex *index);
 void mail_index_data_free(MailIndexData *data);
@@ -38,4 +40,7 @@
 int mail_index_data_record_verify(MailIndexData *data,
 				  MailIndexDataRecord *rec);
 
+/* Return the whole data file mmap()ed. */
+void *mail_index_data_get_mmaped(MailIndexData *data, size_t *size);
+
 #endif
--- a/src/lib-index/mail-index.c	Mon Aug 26 02:22:41 2002 +0300
+++ b/src/lib-index/mail-index.c	Mon Aug 26 02:46:59 2002 +0300
@@ -4,6 +4,7 @@
 #include "ioloop.h"
 #include "hostpid.h"
 #include "mmap-util.h"
+#include "write-full.h"
 #include "mail-index.h"
 #include "mail-index-data.h"
 #include "mail-index-util.h"
@@ -149,6 +150,24 @@
 	return !failed;
 }
 
+int mail_index_fmsync(MailIndex *index, size_t size)
+{
+	i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
+
+	if (msync(index->mmap_base, size, MS_SYNC) == -1) {
+		index_set_error(index, "msync() failed for %s: %m",
+				index->filepath);
+		return FALSE;
+	}
+	if (fsync(index->fd) == -1) {
+		index_set_error(index, "fsync() failed for %s: %m",
+				index->filepath);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 int mail_index_rebuild_all(MailIndex *index)
 {
 	if (!index->rebuild(index))
@@ -309,16 +328,7 @@
 		   when the lock is released, the FSCK flag will also be
 		   removed. */
 		index->header->flags |= MAIL_INDEX_FLAG_FSCK;
-		if (msync(index->mmap_base, sizeof(MailIndexHeader),
-			  MS_SYNC) == -1) {
-			index_set_error(index, "msync() failed for %s: %m",
-					index->filepath);
-			(void)mail_index_set_lock(index, MAIL_LOCK_UNLOCK);
-			return FALSE;
-		}
-		if (fsync(index->fd) == -1) {
-			index_set_error(index, "fsync() failed for %s: %m",
-					index->filepath);
+		if (!mail_index_fmsync(index, sizeof(MailIndexHeader))) {
 			(void)mail_index_set_lock(index, MAIL_LOCK_UNLOCK);
 			return FALSE;
 		}
@@ -515,12 +525,31 @@
 				break;
 		}
 
+		if (hdr.flags & MAIL_INDEX_FLAG_COMPRESS) {
+			/* remove deleted blocks from index file */
+			if (!mail_index_compress(index))
+				break;
+		}
+
+		if (hdr.flags & MAIL_INDEX_FLAG_REBUILD_HASH) {
+			if (!mail_hash_rebuild(index->hash))
+				break;
+		}
+
 		if (hdr.flags & MAIL_INDEX_FLAG_CACHE_FIELDS) {
 			/* need to update cached fields */
 			if (!mail_index_update_cache(index))
 				break;
 		}
 
+		if (hdr.flags & MAIL_INDEX_FLAG_COMPRESS_DATA) {
+			/* remove unused space from index data file.
+			   keep after cache_fields which may move data
+			   and create unused space.. */
+			if (!mail_index_compress_data(index))
+				break;
+		}
+
 		if (!index->sync(index))
 			break;
 		if (!mail_index_open_init(index, update_recent, &hdr))
@@ -582,7 +611,7 @@
         mail_index_init_header(&hdr);
 
 	/* write header */
-	if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+	if (write_full(fd, &hdr, sizeof(hdr)) < 0) {
 		index_set_error(index, "Error writing to temp index %s: %m",
 				path);
 		(void)close(fd);
@@ -728,7 +757,7 @@
 						 unsigned int lookup_seq)
 {
 	MailIndexHeader *hdr;
-	MailIndexRecord *rec, *last_rec;
+	MailIndexRecord *rec, *end_rec;
 	unsigned int seq;
 	off_t seekpos;
 
@@ -764,9 +793,6 @@
 	}
 
 	/* we need to walk through the index to get to wanted position */
-	last_rec = rec + (index->mmap_length-sizeof(MailIndexHeader)) /
-		sizeof(MailIndexRecord);
-
 	if (lookup_seq > index->last_lookup_seq && index->last_lookup != NULL) {
 		/* we want to lookup data after last lookup -
 		   this helps us some */
@@ -781,17 +807,11 @@
 		rec += seq-1 + hdr->first_hole_records;
 	}
 
-	if (rec == last_rec)
-		return NULL;
-
-	while (seq < lookup_seq || rec->uid == 0) {
+	end_rec = (MailIndexRecord *) ((char *) index->mmap_base +
+				       index->mmap_length);
+	while (seq < lookup_seq && rec < end_rec) {
 		if (rec->uid != 0)
 			seq++;
-
-		if (rec == last_rec) {
-			/* out of range */
-			return NULL;
-		}
 		rec++;
 	}
 
@@ -1119,8 +1139,7 @@
 		return FALSE;
 	}
 
-	if (write(index->fd, *rec, sizeof(MailIndexRecord)) !=
-	    sizeof(MailIndexRecord)) {
+	if (write_full(index->fd, *rec, sizeof(MailIndexRecord)) < 0) {
 		index_set_error(index, "Error appending to file %s: %m",
 				index->filepath);
 		return FALSE;
--- a/src/lib-index/mail-index.h	Mon Aug 26 02:22:41 2002 +0300
+++ b/src/lib-index/mail-index.h	Mon Aug 26 02:46:59 2002 +0300
@@ -343,10 +343,13 @@
 void mail_index_close(MailIndex *index);
 int mail_index_rebuild_all(MailIndex *index);
 int mail_index_sync_file(MailIndex *index);
+int mail_index_fmsync(MailIndex *index, size_t size);
 void mail_index_update_headers(MailIndexUpdate *update, IOBuffer *inbuf,
                                MailField cache_fields,
 			       MessageHeaderFunc header_func, void *context);
 int mail_index_update_cache(MailIndex *index);
+int mail_index_compress(MailIndex *index);
+int mail_index_compress_data(MailIndex *index);
 
 /* Max. mmap()ed size for a message */
 #define MAIL_MMAP_BLOCK_SIZE (1024*256)
--- a/src/lib-index/mail-modifylog.c	Mon Aug 26 02:22:41 2002 +0300
+++ b/src/lib-index/mail-modifylog.c	Mon Aug 26 02:46:59 2002 +0300
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "mmap-util.h"
+#include "write-full.h"
 #include "mail-index.h"
 #include "mail-index-util.h"
 #include "mail-modifylog.h"
@@ -181,7 +182,7 @@
 	memset(&hdr, 0, sizeof(hdr));
 	hdr.indexid = log->index->indexid;
 
-	if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+	if (write_full(fd, &hdr, sizeof(hdr)) < 0) {
 		index_set_error(log->index, "write() failed for modify "
 				"log %s: %m", path);
 		return FALSE;
@@ -397,8 +398,7 @@
 		return FALSE;
 	}
 
-	if (write(log->fd, rec, sizeof(ModifyLogRecord)) !=
-	    sizeof(ModifyLogRecord)) {
+	if (write_full(log->fd, rec, sizeof(ModifyLogRecord)) < 0) {
 		index_set_error(log->index, "Error appending to file %s: %m",
 				log->filepath);
 		return FALSE;
--- a/src/lib-storage/flags-file/flags-file.c	Mon Aug 26 02:22:41 2002 +0300
+++ b/src/lib-storage/flags-file/flags-file.c	Mon Aug 26 02:46:59 2002 +0300
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "mmap-util.h"
+#include "write-full.h"
 #include "imap-util.h"
 #include "flags-file.h"
 
@@ -64,7 +65,7 @@
 	}
 
 	/* write the header - it's a 4 byte counter as hex */
-	if (write(ff->fd, buf, HEADER_SIZE) != HEADER_SIZE) {
+	if (write_full(ff->fd, buf, HEADER_SIZE) < 0) {
 		mail_storage_set_critical(ff->storage, "write() failed for "
 					  "flags file %s: %m", ff->path);
 		return FALSE;
@@ -276,7 +277,7 @@
 		}
 	}
 
-	if (write(ff->fd, ff->sync_counter, COUNTER_SIZE) != COUNTER_SIZE) {
+	if (write_full(ff->fd, ff->sync_counter, COUNTER_SIZE) < 0) {
 		mail_storage_set_critical(ff->storage, "write() failed for "
 					  "flags file %s: %m", ff->path);
 		return FALSE;
@@ -321,7 +322,7 @@
 		len--;
 	}
 
-	if ((size_t) write(ff->fd, buf, len) != len) {
+	if (write_full(ff->fd, buf, len) < 0) {
 		mail_storage_set_critical(ff->storage, "write() failed for "
 					  "flags file %s: %m", ff->path);
 		return FALSE;
--- a/src/lib-storage/index/index-save.c	Mon Aug 26 02:22:41 2002 +0300
+++ b/src/lib-storage/index/index-save.c	Mon Aug 26 02:46:59 2002 +0300
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "iobuffer.h"
+#include "write-full.h"
 #include "index-storage.h"
 
 #include <stdlib.h>
@@ -10,19 +11,17 @@
 static int write_with_crlf(int fd, const unsigned char *data,
 			   unsigned int size, unsigned int *last_cr)
 {
-	int i, cr;
-
-	i_assert(size < INT_MAX);
+	unsigned int i, cr;
 
 	cr = *last_cr ? -1 : -2;
-	for (i = 0; i < (int)size; i++) {
+	for (i = 0; i < size; i++) {
 		if (data[i] == '\r')
 			cr = i;
 		else if (data[i] == '\n' && cr != i-1) {
 			/* missing CR */
-			if (write(fd, data, (size_t) i) != i)
+			if (write_full(fd, data, i) < 0)
 				return FALSE;
-			if (write(fd, "\r", 1) != 1)
+			if (write_full(fd, "\r", 1) < 0)
 				return FALSE;
 
 			/* skip the data so far. \n is left into buffer and
@@ -33,7 +32,7 @@
 		}
 	}
 
-	return write(fd, data, size) == i;
+	return write_full(fd, data, size) < 0;
 }
 
 int index_storage_save_into_fd(MailStorage *storage, int fd, const char *path,
--- a/src/lib-storage/index/mbox/mbox-save.c	Mon Aug 26 02:22:41 2002 +0300
+++ b/src/lib-storage/index/mbox/mbox-save.c	Mon Aug 26 02:46:59 2002 +0300
@@ -3,6 +3,7 @@
 #include "lib.h"
 #include "hostpid.h"
 #include "iobuffer.h"
+#include "write-full.h"
 #include "mbox-index.h"
 #include "mbox-lock.h"
 #include "mbox-storage.h"
@@ -40,7 +41,7 @@
 	line = mbox_from_create(sender, internal_date);
 	len = strlen(line);
 
-	return (size_t)write(fd, line, len) == len;
+	return write_full(fd, line, len) < 0;
 }
 
 int mbox_storage_save(Mailbox *box, MailFlags flags, const char *custom_flags[],
--- a/src/lib-storage/subscription-file/subscription-file.c	Mon Aug 26 02:22:41 2002 +0300
+++ b/src/lib-storage/subscription-file/subscription-file.c	Mon Aug 26 02:46:59 2002 +0300
@@ -4,6 +4,7 @@
 
 #include "lib.h"
 #include "mmap-util.h"
+#include "write-full.h"
 #include "imap-match.h"
 #include "mail-storage.h"
 #include "subscription-file.h"
@@ -95,7 +96,7 @@
 		len++;
 	}
 
-	if ((size_t) write(fd, buf, len) != len) {
+	if (write_full(fd, buf, len) < 0) {
 		mail_storage_set_critical(storage, "write() failed for "
 					  "subscription file %s: %m", path);
 		return FALSE;
--- a/src/lib/Makefile.am	Mon Aug 26 02:22:41 2002 +0300
+++ b/src/lib/Makefile.am	Mon Aug 26 02:46:59 2002 +0300
@@ -38,7 +38,8 @@
 	temp-mempool.c \
 	temp-string.c \
 	unlink-directory.c \
-	unlink-lockfiles.c
+	unlink-lockfiles.c \
+	write-full.c
 
 noinst_HEADERS = \
 	base64.h \
@@ -67,7 +68,8 @@
 	temp-mempool.h \
 	temp-string.h \
 	unlink-directory.h \
-	unlink-lockfiles.h
+	unlink-lockfiles.h \
+	write-full.h
 
 EXTRA_DIST = \
 	$(ioloop_sources)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/write-full.c	Mon Aug 26 02:46:59 2002 +0300
@@ -0,0 +1,49 @@
+/*
+ write-full.c - Simpler API to write to file
+
+    Copyright (c) 2002 Timo Sirainen
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+
+int write_full(int fd, const void *data, size_t size)
+{
+	ssize_t ret;
+
+	do {
+		ret = write(fd, data, size < INT_MAX ? size : INT_MAX);
+		if (ret < 0)
+			return -1;
+
+		if (ret == 0) {
+			/* nothing was written, only reason for this should
+			   be out of disk space */
+			errno = ENOSPC;
+			return -1;
+		}
+		size -= ret;
+	} while (size > 0);
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/write-full.h	Mon Aug 26 02:46:59 2002 +0300
@@ -0,0 +1,9 @@
+#ifndef __WRITE_FULL_H
+#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
+   it's unspecified how much data was actually written. */
+int write_full(int fd, const void *data, size_t size);
+
+#endif