changeset 364:ea958a5b9de1 HEAD

Added io_buffer_set_start_offset() and io_buffer_ref() and replaced io_buffer_destroy() with io_buffer_unref(). mbox file is now kept open all the time.
author Timo Sirainen <tss@iki.fi>
date Sun, 06 Oct 2002 08:44:27 +0300
parents 567e932cdc66
children cb405d2f5fd5
files src/auth/master.c src/auth/userinfo-passwd-file.c src/imap/client.c src/lib-imap/imap-message-cache.c src/lib-index/mail-index-update-cache.c src/lib-index/mail-index-util.c src/lib-index/mail-index.h src/lib-index/maildir/maildir-update.c src/lib-index/mbox/mbox-fsck.c src/lib-index/mbox/mbox-index.c src/lib-index/mbox/mbox-index.h src/lib-index/mbox/mbox-open.c src/lib-index/mbox/mbox-rebuild.c src/lib-index/mbox/mbox-rewrite.c src/lib-storage/index/index-copy.c src/lib-storage/index/index-fetch.c src/lib-storage/index/index-fetch.h src/lib-storage/index/index-msgcache.c src/lib-storage/index/index-search.c src/lib-storage/index/mbox/mbox-expunge.c src/lib-storage/index/mbox/mbox-save.c src/lib/iobuffer.c src/lib/iobuffer.h src/login/auth-connection.c src/login/client.c src/master/auth-process.c src/master/login-process.c src/master/settings.c
diffstat 28 files changed, 232 insertions(+), 172 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/master.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/auth/master.c	Sun Oct 06 08:44:27 2002 +0300
@@ -77,6 +77,6 @@
 
 void master_deinit(void)
 {
-	io_buffer_destroy(outbuf);
+	io_buffer_unref(outbuf);
 	io_remove(io_master);
 }
--- a/src/auth/userinfo-passwd-file.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/auth/userinfo-passwd-file.c	Sun Oct 06 08:44:27 2002 +0300
@@ -318,7 +318,7 @@
 		}
 		t_pop();
 	}
-	io_buffer_destroy(inbuf);
+	io_buffer_unref(inbuf);
 }
 
 static PasswdFile *passwd_file_parse(const char *path)
--- a/src/imap/client.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/imap/client.c	Sun Oct 06 08:44:27 2002 +0300
@@ -95,8 +95,8 @@
 	imap_parser_destroy(client->parser);
 	io_remove(client->io);
 
-	io_buffer_destroy(client->inbuf);
-	io_buffer_destroy(client->outbuf);
+	io_buffer_unref(client->inbuf);
+	io_buffer_unref(client->outbuf);
 
 	i_free(client);
 
--- a/src/lib-imap/imap-message-cache.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-imap/imap-message-cache.c	Sun Oct 06 08:44:27 2002 +0300
@@ -348,7 +348,7 @@
 void imap_msgcache_close(ImapMessageCache *cache)
 {
 	if (cache->open_inbuf != NULL) {
-		io_buffer_destroy(cache->open_inbuf);
+		io_buffer_unref(cache->open_inbuf);
 		cache->open_inbuf = NULL;
 	}
 
--- a/src/lib-index/mail-index-update-cache.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-index/mail-index-update-cache.c	Sun Oct 06 08:44:27 2002 +0300
@@ -23,8 +23,7 @@
 	mail_index_update_headers(update, inbuf, cache_fields, NULL, NULL);
 	failed = !index->update_end(update);
 
-	io_buffer_destroy(inbuf);
-
+	io_buffer_unref(inbuf);
 	return !failed;
 }
 
--- a/src/lib-index/mail-index-util.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-index/mail-index-util.c	Sun Oct 06 08:44:27 2002 +0300
@@ -155,6 +155,6 @@
 	message_get_body_size(inbuf, &body_size, (uoff_t)-1);
 	*virtual_size = body_size.virtual_size;
 
-	io_buffer_destroy(inbuf);
+	io_buffer_unref(inbuf);
 	return TRUE;
 }
--- a/src/lib-index/mail-index.h	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-index/mail-index.h	Sun Oct 06 08:44:27 2002 +0300
@@ -319,6 +319,7 @@
 
 	char *mbox_path; /* mbox-specific path to the actual mbox file */
 	uoff_t mbox_size; /* last synced size of mbox file */
+	int mbox_fd;
 	int mbox_locks;
 
 	int fd; /* opened index file */
@@ -354,7 +355,7 @@
 #define MAIL_INDEX_PRIVATE_FILL \
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
-	0, 0, 0, 0, 0, 0, 0, 0, 0
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 
 /* defaults - same as above but prefixed with mail_index_. */
 int mail_index_open(MailIndex *index, int update_recent, int fast);
--- a/src/lib-index/maildir/maildir-update.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-index/maildir/maildir-update.c	Sun Oct 06 08:44:27 2002 +0300
@@ -13,6 +13,6 @@
 	inbuf = io_buffer_create_mmap(fd, default_pool,
 				      MAIL_MMAP_BLOCK_SIZE, 0, FALSE);
 	mail_index_update_headers(update, inbuf, 0, NULL, NULL);
-	io_buffer_destroy(inbuf);
+	io_buffer_unref(inbuf);
 	return TRUE;
 }
--- a/src/lib-index/mbox/mbox-fsck.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-index/mbox/mbox-fsck.c	Sun Oct 06 08:44:27 2002 +0300
@@ -287,25 +287,19 @@
 int mbox_index_fsck(MailIndex *index)
 {
 	IOBuffer *inbuf;
-	int fd, failed;
+	int failed;
 
-	/* open the mbox file. we don't really need to open it read-write,
-	   but fcntl() locking requires it. */
-	fd = open(index->mbox_path, O_RDWR);
-	if (fd == -1)
-		return mbox_set_syscall_error(index, "open()");
+	inbuf = mbox_file_open(index, 0, TRUE);
+	if (inbuf == NULL)
+		return FALSE;
 
-	inbuf = io_buffer_create_mmap(fd, default_pool,
-				      MAIL_MMAP_BLOCK_SIZE, 0, TRUE);
-
-	if (!mbox_lock(index, index->mbox_path, fd, FALSE))
+	if (!mbox_lock(index, index->mbox_path, index->mbox_fd, FALSE))
 		failed = TRUE;
 	else {
 		failed = !mbox_index_fsck_buf(index, inbuf);
-		(void)mbox_unlock(index, index->mbox_path, fd);
+		(void)mbox_unlock(index, index->mbox_path, index->mbox_fd);
 	}
-
-	io_buffer_destroy(inbuf);
+	io_buffer_unref(inbuf);
 
 	if (failed)
 		return FALSE;
--- a/src/lib-index/mbox/mbox-index.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-index/mbox/mbox-index.c	Sun Oct 06 08:44:27 2002 +0300
@@ -8,6 +8,9 @@
 #include "mail-index-data.h"
 #include "mail-custom-flags.h"
 
+#include <fcntl.h>
+#include <unistd.h>
+
 extern MailIndex mbox_index;
 
 int mbox_set_syscall_error(MailIndex *index, const char *function)
@@ -19,6 +22,38 @@
 	return FALSE;
 }
 
+IOBuffer *mbox_file_open(MailIndex *index, uoff_t offset, int reopen)
+{
+	i_assert(offset < OFF_T_MAX);
+
+	if (reopen)
+		mbox_file_close(index);
+
+	if (index->mbox_fd == -1) {
+		index->mbox_fd = open(index->mbox_path, O_RDWR);
+		if (index->mbox_fd == -1) {
+			mbox_set_syscall_error(index, "open()");
+			return NULL;
+		}
+	}
+
+	if (lseek(index->mbox_fd, (off_t)offset, SEEK_SET) != (off_t)offset) {
+		mbox_set_syscall_error(index, "lseek()");
+		return NULL;
+	}
+
+	return io_buffer_create_mmap(index->mbox_fd, default_pool,
+				     MAIL_MMAP_BLOCK_SIZE, 0, FALSE);
+}
+
+void mbox_file_close(MailIndex *index)
+{
+	if (index->mbox_fd != -1) {
+		close(index->mbox_fd);
+		index->mbox_fd = -1;
+	}
+}
+
 void mbox_header_init_context(MboxHeaderContext *ctx, MailIndex *index)
 {
 	memset(ctx, 0, sizeof(MboxHeaderContext));
@@ -425,6 +460,7 @@
 	memcpy(index, &mbox_index, sizeof(MailIndex));
 
 	index->fd = -1;
+	index->mbox_fd = -1;
 	index->dir = i_strdup(dir);
 
 	len = strlen(index->dir);
@@ -437,6 +473,7 @@
 
 static void mbox_index_free(MailIndex *index)
 {
+        mbox_file_close(index);
 	mail_index_close(index);
 	i_free(index->dir);
 	i_free(index);
--- a/src/lib-index/mbox/mbox-index.h	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-index/mbox/mbox-index.h	Sun Oct 06 08:44:27 2002 +0300
@@ -14,6 +14,12 @@
 
 int mbox_set_syscall_error(MailIndex *index, const char *function);
 
+/* Make sure the mbox is opened. If reopen is TRUE, the file is closed first,
+   which is useful when you want to be sure you're not accessing a deleted
+   mbox file. */
+IOBuffer *mbox_file_open(MailIndex *index, uoff_t offset, int reopen);
+void mbox_file_close(MailIndex *index);
+
 void mbox_header_init_context(MboxHeaderContext *ctx, MailIndex *index);
 void mbox_header_free_context(MboxHeaderContext *ctx);
 void mbox_header_func(MessagePart *part __attr_unused__,
--- a/src/lib-index/mbox/mbox-open.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-index/mbox/mbox-open.c	Sun Oct 06 08:44:27 2002 +0300
@@ -11,10 +11,11 @@
 
 IOBuffer *mbox_open_mail(MailIndex *index, MailIndexRecord *rec)
 {
+	IOBuffer *inbuf;
 	uoff_t offset, stop_offset;
-	off_t pos;
-	char buf[7], *p;
-	int fd, ret, failed;
+	unsigned char *data;
+	size_t size;
+	int failed;
 
 	i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
 
@@ -27,71 +28,54 @@
 
 	stop_offset = offset + rec->header_size + rec->body_size;
 
-	fd = open(index->mbox_path, O_RDONLY);
-	if (fd == -1) {
-		mbox_set_syscall_error(index, "open()");
+	inbuf = mbox_file_open(index, offset, FALSE);
+	if (inbuf == NULL)
 		return NULL;
-	}
 
-	pos = lseek(fd, (off_t)offset, SEEK_SET);
-	if (pos == -1) {
-		mbox_set_syscall_error(index, "lseek()");
-		(void)close(fd);
+	/* make sure message size is valid - it must end with
+	   either EOF or "\nFrom "*/
+	if (!io_buffer_seek(inbuf, stop_offset - offset)) {
+		mbox_set_syscall_error(index, "io_buffer_seek()");
+		io_buffer_unref(inbuf);
 		return NULL;
 	}
 
-	failed = TRUE;
-	if ((uoff_t)pos == offset) {
-		/* make sure message size is valid */
-		if (lseek(fd, (off_t)stop_offset, SEEK_SET) ==
-		    (off_t)stop_offset) {
-			/* and check that we end with either EOF or to
-			   beginning of next message */
-			ret = read(fd, buf, 7);
-			if (ret >= 6) {
-				/* "[\r]\nFrom " expected */
-				if (buf[0] != '\r')
-					p = buf;
-				else {
-					p = buf+1;
-					ret--;
-				}
-				if (ret >= 6 && strncmp(p, "\nFrom ", 6) == 0)
-					failed = FALSE;
-			} else {
-				p = buf;
-				if (ret > 0 && *p == '\r') {
-					p++;
-					ret--;
-				}
-				if (ret > 0 && *p == '\n')
-					ret--;
+	(void)io_buffer_read_data_blocking(inbuf, &data, &size, 6);
+	if (size >= 6) {
+		/* "[\r]\nFrom " expected */
+		if (data[0] == '\r') {
+			data++;
+			size--;
+		}
 
-				if (ret == 0)
-					failed = FALSE; /* end of file */
-			}
+		failed = size < 6 || strncmp((char *) data, "\nFrom ", 6) != 0;
+	} else {
+		if (size > 0 && data[0] == '\r') {
+			data++;
+			size--;
 		}
+		if (size > 0 && data[0] == '\n')
+			size--;
+
+                /* we should be at end of file now */
+		failed = size != 0;
 	}
 
-	if (!failed) {
-		if (lseek(fd, (off_t)offset, SEEK_SET) < 0) {
-			mbox_set_syscall_error(index, "lseek()");
-			failed = TRUE;
-		}
-	} else {
+	if (!io_buffer_seek(inbuf, 0)) {
+		mbox_set_syscall_error(index, "io_buffer_seek()");
+		failed = TRUE;
+	}
+
+	if (failed) {
 		/* file has been updated, rescan it */
 		index->set_flags |= MAIL_INDEX_FLAG_FSCK;
 
 		index_set_error(index, "mbox file %s was modified "
 				"unexpectedly, fscking", index->mbox_path);
+		io_buffer_unref(inbuf);
+		return NULL;
 	}
 
-	if (failed) {
-		(void)close(fd);
-		return NULL;
-	} else {
-		return io_buffer_create_mmap(fd, default_pool,
-					     MAIL_MMAP_BLOCK_SIZE,
-					     stop_offset - offset, TRUE);
-	}
+	io_buffer_set_read_limit(inbuf, stop_offset - offset);
+	return inbuf;
 }
--- a/src/lib-index/mbox/mbox-rebuild.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-index/mbox/mbox-rebuild.c	Sun Oct 06 08:44:27 2002 +0300
@@ -16,7 +16,7 @@
 {
 	IOBuffer *inbuf;
 	struct stat st;
-	int fd, failed;
+	int failed;
 
 	i_assert(index->lock_type != MAIL_LOCK_SHARED);
 
@@ -42,26 +42,21 @@
 	if (!mail_index_data_reset(index->data))
 		return FALSE;
 
-	/* open the mbox file. we don't really need to open it read-write,
-	   but fcntl() locking requires it. */
-	fd = open(index->mbox_path, O_RDWR);
-	if (fd == -1)
-		return mbox_set_syscall_error(index, "open()");
+	inbuf = mbox_file_open(index, 0, TRUE);
+	if (inbuf == NULL)
+		return FALSE;
 
 	/* lock the mailbox so we can be sure no-one interrupts us. */
-	if (!mbox_lock(index, index->mbox_path, fd, FALSE)) {
-		if (close(fd) < 0)
-			mbox_set_syscall_error(index, "close()");
+	if (!mbox_lock(index, index->mbox_path, index->mbox_fd, FALSE)) {
+		io_buffer_unref(inbuf);
 		return FALSE;
 	}
 
-	inbuf = io_buffer_create_mmap(fd, default_pool,
-				      MAIL_MMAP_BLOCK_SIZE, 0, TRUE);
 	mbox_skip_empty_lines(inbuf);
 	failed = !mbox_index_append(index, inbuf);
+	(void)mbox_unlock(index, index->mbox_path, index->mbox_fd);
 
-	(void)mbox_unlock(index, index->mbox_path, fd);
-	io_buffer_destroy(inbuf);
+	io_buffer_unref(inbuf);
 
 	if (failed)
 		return FALSE;
--- a/src/lib-index/mbox/mbox-rewrite.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-index/mbox/mbox-rewrite.c	Sun Oct 06 08:44:27 2002 +0300
@@ -355,8 +355,8 @@
 		ret = ftruncate(out_fd, (off_t) (out_offset + inbuf->size));
 	}
 
-	io_buffer_destroy(outbuf);
-	io_buffer_destroy(inbuf);
+	io_buffer_unref(outbuf);
+	io_buffer_unref(inbuf);
 
 	return ret;
 }
@@ -372,7 +372,7 @@
 	uoff_t offset, dirty_offset;
 	const char *path;
 	unsigned int seq;
-	int mbox_fd, tmp_fd, failed, dirty_found, locked, rewrite;
+	int tmp_fd, failed, dirty_found, locked, rewrite;
 
 	i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
 
@@ -381,18 +381,16 @@
 		return TRUE;
 	}
 
-	mbox_fd = tmp_fd = -1; locked = FALSE;
+	tmp_fd = -1; locked = FALSE;
 	failed = TRUE; rewrite = FALSE;
 	do {
 		/* lock before fscking to prevent race conditions between
 		   fsck's unlock and our lock. */
-		mbox_fd = open(index->mbox_path, O_RDWR);
-		if (mbox_fd == -1) {
-			mbox_set_syscall_error(index, "open()");
+		inbuf = mbox_file_open(index, 0, TRUE);
+		if (inbuf == NULL)
 			break;
-		}
 
-		if (!mbox_lock(index, index->mbox_path, mbox_fd, TRUE))
+		if (!mbox_lock(index, index->mbox_path, index->mbox_fd, TRUE))
 			break;
 		locked = TRUE;
 
@@ -415,10 +413,12 @@
 	} while (0);
 
 	if (!rewrite) {
-		if (locked)
-			(void)mbox_unlock(index, index->mbox_path, mbox_fd);
-		if (mbox_fd != -1 && close(mbox_fd) < 0)
-			mbox_set_syscall_error(index, "close()");
+		if (locked) {
+			(void)mbox_unlock(index, index->mbox_path,
+					  index->mbox_fd);
+		}
+		if (inbuf != NULL)
+			io_buffer_unref(inbuf);
 		return !failed;
 	}
 
@@ -430,8 +430,6 @@
 	}
 	dirty_offset = 0;
 
-	inbuf = io_buffer_create_mmap(mbox_fd, default_pool,
-				      MAIL_MMAP_BLOCK_SIZE, 0, FALSE);
 	outbuf = io_buffer_create_file(tmp_fd, default_pool, 8192, FALSE);
 
 	failed = FALSE; seq = 1;
@@ -500,8 +498,8 @@
 		failed = TRUE;
 	}
 
-	io_buffer_destroy(outbuf);
-	io_buffer_destroy(inbuf);
+	io_buffer_unref(inbuf);
+	io_buffer_unref(outbuf);
 
 	if (!failed) {
 		/* POSSIBLE DATA LOSS HERE. We're writing to the mbox file,
@@ -519,7 +517,7 @@
 
 		   Also, we might as well be shrinking the file, in which
 		   case we can't lose data. */
-		if (fd_copy(tmp_fd, mbox_fd, dirty_offset) == 0) {
+		if (fd_copy(tmp_fd, index->mbox_fd, dirty_offset) == 0) {
 			/* all ok, we need to fsck the index next time.
 			   use set_flags because set_lock() would remove it
 			   if we modified it directly */
@@ -531,11 +529,9 @@
 		}
 	}
 
-	(void)mbox_unlock(index, index->mbox_path, mbox_fd);
+	(void)mbox_unlock(index, index->mbox_path, index->mbox_fd);
 	(void)unlink(path);
 
-	if (close(mbox_fd) < 0)
-		mbox_set_syscall_error(index, "close()");
 	if (close(tmp_fd) < 0)
 		index_file_set_syscall_error(index, path, "close()");
 	return failed;
--- a/src/lib-storage/index/index-copy.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-storage/index/index-copy.c	Sun Oct 06 08:44:27 2002 +0300
@@ -30,7 +30,7 @@
 				  ctx->custom_flags, rec->internal_date,
 				  inbuf, inbuf->size);
 
-	io_buffer_destroy(inbuf);
+	io_buffer_unref(inbuf);
 	return !failed;
 }
 
--- a/src/lib-storage/index/index-fetch.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-storage/index/index-fetch.c	Sun Oct 06 08:44:27 2002 +0300
@@ -249,6 +249,9 @@
 	unsigned int orig_len;
 	int failed, data_written;
 
+	if ((rec->msg_flags & MAIL_SEEN) == 0)
+		ctx->found_unseen = TRUE;
+
 	ctx->str = t_string_new(2048);
 
 	t_string_printfa(ctx->str, "* %u FETCH (", client_seq);
@@ -352,13 +355,15 @@
 	ctx.fetch_data = fetch_data;
 	ctx.outbuf = outbuf;
 
-	/* If we have any BODY[..] sections, \Seen flag is added for
-	   all messages */
-	sect = ctx.fetch_data->body_sections;
-	for (; sect != NULL; sect = sect->next) {
-		if (!sect->peek) {
-			ctx.update_seen = TRUE;
-			break;
+	if (!box->readonly) {
+		/* If we have any BODY[..] sections, \Seen flag is added for
+		   all messages */
+		sect = ctx.fetch_data->body_sections;
+		for (; sect != NULL; sect = sect->next) {
+			if (!sect->peek) {
+				ctx.update_seen = TRUE;
+				break;
+			}
 		}
 	}
 
@@ -374,7 +379,7 @@
 	if (all_found != NULL)
 		*all_found = ret == 1;
 
-	if (ret >= 1 && ctx.update_seen && !box->readonly) {
+	if (ret >= 1 && ctx.update_seen && ctx.found_unseen) {
 		/* BODY[..] was fetched, set \Seen flag for all messages.
 		   This needs to be done separately because we need exclusive
 		   lock for it */
--- a/src/lib-storage/index/index-fetch.h	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-storage/index/index-fetch.h	Sun Oct 06 08:44:27 2002 +0300
@@ -11,7 +11,7 @@
 	MailFetchData *fetch_data;
 	IOBuffer *outbuf;
 	TempString *str;
-	int update_seen;
+	int update_seen, found_unseen;
 	int first;
 } FetchContext;
 
--- a/src/lib-storage/index/index-msgcache.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-storage/index/index-msgcache.c	Sun Oct 06 08:44:27 2002 +0300
@@ -37,7 +37,7 @@
 	if (!io_buffer_seek(inbuf, 0)) {
 		i_error("index_msgcache_inbuf_rewind: lseek() failed: %m");
 
-		io_buffer_destroy(inbuf);
+		io_buffer_unref(inbuf);
 		return NULL;
 	}
 
--- a/src/lib-storage/index/index-search.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-storage/index/index-search.c	Sun Oct 06 08:44:27 2002 +0300
@@ -551,7 +551,7 @@
 				      search_text_body);
 	}
 
-	io_buffer_destroy(inbuf);
+	io_buffer_unref(inbuf);
 	return TRUE;
 }
 
--- a/src/lib-storage/index/mbox/mbox-expunge.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-storage/index/mbox/mbox-expunge.c	Sun Oct 06 08:44:27 2002 +0300
@@ -107,7 +107,7 @@
 	MailIndexRecord *rec;
 	IOBuffer *inbuf, *outbuf;
 	unsigned int seq;
-	int fd, failed;
+	int failed;
 
 	if (!index_expunge_seek_first(ibox, &seq, &rec))
 		return FALSE;
@@ -117,22 +117,17 @@
 		return TRUE;
 	}
 
-	fd = open(ibox->index->mbox_path, O_RDWR);
-	if (fd == -1) {
-		mail_storage_set_error(ibox->box.storage,
-				       "Error opening mbox file %s: %m",
-				       ibox->index->mbox_path);
+	inbuf = mbox_file_open(ibox->index, 0, TRUE);
+	if (inbuf == NULL)
+		return FALSE;
+
+	if (!mbox_lock(ibox->index, ibox->index->mbox_path,
+		       ibox->index->mbox_fd, TRUE)) {
+		io_buffer_unref(inbuf);
 		return FALSE;
 	}
 
-	if (!mbox_lock(ibox->index, ibox->index->mbox_path, fd, TRUE)) {
-		(void)close(fd);
-		return FALSE;
-	}
-
-	inbuf = io_buffer_create_mmap(fd, default_pool,
-				      MAIL_MMAP_BLOCK_SIZE, 0, FALSE);
-	outbuf = io_buffer_create_file(fd, default_pool, 4096, FALSE);
+	outbuf = io_buffer_create_file(inbuf->fd, default_pool, 4096, FALSE);
 
 	failed = !expunge_real(ibox, rec, seq, inbuf, outbuf,
 			       expunge_func, context);
@@ -152,10 +147,9 @@
 		failed = TRUE;
 	}
 
-	(void)mbox_unlock(ibox->index, ibox->index->mbox_path, fd);
-	(void)close(fd);
-	io_buffer_destroy(inbuf);
-	io_buffer_destroy(outbuf);
+	(void)mbox_unlock(ibox->index, ibox->index->mbox_path,
+			  ibox->index->mbox_fd);
+	io_buffer_unref(outbuf);
 
 	return !failed;
 }
--- a/src/lib-storage/index/mbox/mbox-save.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib-storage/index/mbox/mbox-save.c	Sun Oct 06 08:44:27 2002 +0300
@@ -150,6 +150,7 @@
 	IndexMailbox *ibox = (IndexMailbox *) box;
 	MailFlags real_flags;
 	const char *mbox_path;
+	IOBuffer *inbuf;
 	int fd, failed;
 	off_t pos;
 
@@ -164,13 +165,13 @@
 	if (!index_mailbox_fix_custom_flags(ibox, &real_flags, custom_flags))
 		return FALSE;
 
-	/* append the data into mbox file */
-	fd = open(ibox->index->mbox_path, O_RDWR | O_CREAT, 0660);
-	if (fd == -1) {
-		mail_storage_set_critical(box->storage, "Can't open mbox file "
-					  "%s: %m", ibox->index->mbox_path);
+	/* just make sure the mbox is opened, we don't need the iobuffer */
+	inbuf = mbox_file_open(ibox->index, 0, TRUE);
+	if (inbuf == NULL)
 		return FALSE;
-	}
+
+	io_buffer_unref(inbuf);
+	fd = ibox->index->mbox_fd;
 
 	if (!mbox_lock(ibox->index, ibox->index->mbox_path, fd, TRUE)) {
 		(void)close(fd);
@@ -203,6 +204,5 @@
 	}
 
 	(void)mbox_unlock(ibox->index, ibox->index->mbox_path, fd);
-	(void)close(fd);
 	return !failed;
 }
--- a/src/lib/iobuffer.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib/iobuffer.c	Sun Oct 06 08:44:27 2002 +0300
@@ -58,6 +58,7 @@
         i_assert(pool != NULL);
 
 	buf = p_new(pool, IOBuffer, 1);
+	buf->refcount = 1;
 	buf->fd = fd;
 	buf->pool = pool;
 	buf->priority = priority;
@@ -126,11 +127,20 @@
 	return buf;
 }
 
-void io_buffer_destroy(IOBuffer *buf)
+void io_buffer_ref(IOBuffer *buf)
+{
+	buf->refcount++;
+}
+
+void io_buffer_unref(IOBuffer *buf)
 {
 	if (buf == NULL)
 		return;
 
+	i_assert(buf->refcount > 0);
+	if (--buf->refcount > 0)
+		return;
+
         if (buf->io != NULL)
 		io_remove(buf->io);
 	if (buf->buffer != NULL) {
@@ -645,9 +655,12 @@
 
 static ssize_t io_buffer_set_mmaped_pos(IOBuffer *buf)
 {
-	buf->pos = buf->buffer_size;
-	if (buf->pos - buf->skip > buf->limit - buf->offset)
-		buf->pos = buf->limit - buf->offset + buf->skip;
+	i_assert((uoff_t)buf->mmap_offset <= buf->start_offset + buf->limit);
+
+	buf->pos = buf->start_offset + buf->limit - buf->mmap_offset;
+	if (buf->pos > buf->buffer_size)
+		buf->pos = buf->buffer_size;
+
 	return buf->pos - buf->skip;
 }
 
@@ -703,18 +716,37 @@
 	return io_buffer_set_mmaped_pos(buf);
 }
 
-void io_buffer_set_read_limit(IOBuffer *inbuf, uoff_t offset)
+void io_buffer_set_start_offset(IOBuffer *buf, uoff_t offset)
 {
-	i_assert(offset <= inbuf->size);
+	off_t diff;
+
+	i_assert(offset <= buf->size);
+
+	if (offset == buf->start_offset)
+		return;
+
+	diff = (off_t)buf->start_offset - (off_t)offset;
+	buf->start_offset = offset;
+	buf->size += diff;
+	buf->limit += diff;
+
+	io_buffer_reset(buf);
+
+	buf->skip = buf->pos = buf->start_offset;
+}
+
+void io_buffer_set_read_limit(IOBuffer *buf, uoff_t offset)
+{
+	i_assert(offset <= buf->size);
 
 	if (offset == 0)
-		inbuf->limit = inbuf->size;
+		buf->limit = buf->size;
 	else {
-		i_assert(offset >= inbuf->offset);
+		i_assert(offset >= buf->offset);
 
-		inbuf->limit = offset;
-		if (inbuf->offset + (inbuf->pos - inbuf->skip) > offset)
-			inbuf->pos = offset - inbuf->offset + inbuf->skip;
+		buf->limit = offset;
+		if (buf->offset + (buf->pos - buf->skip) > offset)
+			buf->pos = offset - buf->offset + buf->skip;
 	}
 }
 
@@ -867,9 +899,12 @@
 int io_buffer_seek(IOBuffer *buf, uoff_t offset)
 {
 	uoff_t real_offset;
+	off_t ret;
 
-	if (buf->closed)
+	if (buf->closed) {
+		errno = EBADF;
 		return FALSE;
+	}
 
 	real_offset = buf->start_offset + offset;
 	if (real_offset > OFF_T_MAX) {
@@ -885,9 +920,14 @@
 		   pick up from there */
 		buf->pos = buf->skip = real_offset;
 	} else {
-		if (lseek(buf->fd, (off_t)real_offset, SEEK_SET) !=
-		    (off_t)real_offset)
+		ret = lseek(buf->fd, (off_t)real_offset, SEEK_SET);
+		if (ret < 0)
 			return FALSE;
+
+		if (ret != (off_t)real_offset) {
+			errno = EINVAL;
+			return FALSE;
+		}
 	}
 
 	buf->offset = offset;
--- a/src/lib/iobuffer.h	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/lib/iobuffer.h	Sun Oct 06 08:44:27 2002 +0300
@@ -17,6 +17,7 @@
 /* private: */
 	Pool pool;
 	IO io;
+	int refcount;
 	int priority;
 
 	int timeout_msecs;
@@ -56,8 +57,12 @@
    stop reading, or 0 to end of file. */
 IOBuffer *io_buffer_create_mmap(int fd, Pool pool, size_t block_size,
 				uoff_t size, int autoclose_fd);
-/* Destroy a buffer. */
-void io_buffer_destroy(IOBuffer *buf);
+
+/* Reference counting. References start from 1, so calling io_buffer_unref()
+   destroys the buffer if io_buffer_ref() is never used. */
+void io_buffer_ref(IOBuffer *buf);
+void io_buffer_unref(IOBuffer *buf);
+
 /* Mark the buffer closed. Any sends/reads after this will return -1.
    The data already in buffer can be used, and the remaining output buffer
    will be sent. */
@@ -108,9 +113,13 @@
 void io_buffer_send_flush_callback(IOBuffer *buf, IOBufferFlushFunc func,
 				   void *context);
 
+/* Change the start_offset and call io_buffer_reset(). Doesn't do anything
+   if offset is the same as existing start_offset. */
+void io_buffer_set_start_offset(IOBuffer *buf, uoff_t offset);
+
 /* IO buffer won't be read past specified offset. Giving 0 as offset removes
    the limit. */
-void io_buffer_set_read_limit(IOBuffer *inbuf, uoff_t offset);
+void io_buffer_set_read_limit(IOBuffer *buf, uoff_t offset);
 
 /* Returns number of bytes read if read was ok,
    -1 if disconnected / EOF, -2 if the buffer is full */
--- a/src/login/auth-connection.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/login/auth-connection.c	Sun Oct 06 08:44:27 2002 +0300
@@ -118,8 +118,8 @@
 
 	(void)close(conn->fd);
 	io_remove(conn->io);
-	io_buffer_destroy(conn->inbuf);
-	io_buffer_destroy(conn->outbuf);
+	io_buffer_unref(conn->inbuf);
+	io_buffer_unref(conn->outbuf);
 	i_free(conn->path);
 	i_free(conn);
 }
--- a/src/login/client.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/login/client.c	Sun Oct 06 08:44:27 2002 +0300
@@ -289,8 +289,8 @@
 	if (--client->refcount > 0)
 		return TRUE;
 
-	io_buffer_destroy(client->inbuf);
-	io_buffer_destroy(client->outbuf);
+	io_buffer_unref(client->inbuf);
+	io_buffer_unref(client->outbuf);
 
 	i_free(client->tag);
 	i_free(client->plain_login);
--- a/src/master/auth-process.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/master/auth-process.c	Sun Oct 06 08:44:27 2002 +0300
@@ -154,7 +154,7 @@
 
 	(void)unlink(t_strconcat(set_login_dir, "/", p->name, NULL));
 
-	io_buffer_destroy(p->outbuf);
+	io_buffer_unref(p->outbuf);
 	io_remove(p->io);
 	(void)close(p->fd);
 	i_free(p->name);
--- a/src/master/login-process.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/master/login-process.c	Sun Oct 06 08:44:27 2002 +0300
@@ -165,7 +165,7 @@
 	if (--p->refcount > 0)
 		return;
 
-	io_buffer_destroy(p->outbuf);
+	io_buffer_unref(p->outbuf);
 	i_free(p);
 }
 
--- a/src/master/settings.c	Sun Oct 06 06:09:36 2002 +0300
+++ b/src/master/settings.c	Sun Oct 06 08:44:27 2002 +0300
@@ -338,7 +338,7 @@
 		}
 	};
 
-	io_buffer_destroy(inbuf);
+	io_buffer_unref(inbuf);
 
         settings_verify();
 }