changeset 7331:9ef06104648a HEAD

MAIL_INDEX_MAIL_FLAG_BACKEND specifies if file should be in alt dir or primary dir. If the flag is changed, the file is moved.
author Timo Sirainen <tss@iki.fi>
date Tue, 04 Mar 2008 04:07:32 +0200
parents 93f67b71476c
children 3f9f01913f90
files src/lib-storage/index/dbox/dbox-file.c src/lib-storage/index/dbox/dbox-file.h src/lib-storage/index/dbox/dbox-storage.c src/lib-storage/index/dbox/dbox-storage.h src/lib-storage/index/dbox/dbox-sync-file.c src/lib-storage/index/dbox/dbox-sync.c
diffstat 6 files changed, 182 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/dbox/dbox-file.c	Tue Mar 04 04:05:35 2008 +0200
+++ b/src/lib-storage/index/dbox/dbox-file.c	Tue Mar 04 04:07:32 2008 +0200
@@ -7,6 +7,8 @@
 #include "hostpid.h"
 #include "istream.h"
 #include "ostream.h"
+#include "mkdir-parents.h"
+#include "fdatasync-path.h"
 #include "write-full.h"
 #include "str.h"
 #include "dbox-storage.h"
@@ -375,6 +377,7 @@
 static int dbox_file_open_fd(struct dbox_file *file)
 {
 	const char *path;
+	bool alt = FALSE;
 	int i;
 
 	/* try the primary path first */
@@ -398,7 +401,11 @@
 		/* try the alternative path */
 		path = t_strdup_printf("%s/%s", file->mbox->alt_path,
 				       file->fname);
+		alt = TRUE;
 	}
+	i_free(file->current_path);
+	file->current_path = i_strdup(path);
+	file->alt_path = alt;
 	return 1;
 }
 
@@ -1015,7 +1022,7 @@
 	} else {
 		i_error("%s: Metadata changed unexpectedly",
 			dbox_file_get_path(file));
-		ret = 0;
+		ret = -1;
 	}
 
 	dbox_index_unlock_file(file->mbox->dbox_index, file->file_id);
@@ -1199,6 +1206,113 @@
 	return TRUE;
 }
 
+int dbox_file_move(struct dbox_file *file, bool alt_path)
+{
+	struct ostream *output;
+	const char *dest_dir, *temp_path, *dest_path;
+	struct stat st;
+	bool deleted;
+	int out_fd, ret = 0;
+
+	i_assert(file->input != NULL);
+
+	if (file->alt_path == alt_path)
+		return 0;
+
+	if (stat(file->current_path, &st) < 0 && errno == ENOENT) {
+		/* already expunged by another session */
+		return 0;
+	}
+
+	dest_dir = alt_path ? file->mbox->alt_path : file->mbox->path;
+	temp_path = t_strdup_printf("%s/%s", dest_dir,
+				    dbox_generate_tmp_filename());
+
+	/* first copy the file. make sure to catch every possible error
+	   since we really don't want to break the file. */
+	out_fd = open(temp_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+	if (out_fd == -1 && errno == ENOENT) {
+		if (mkdir_parents(dest_dir, 0700) < 0) {
+			i_error("mkdir_parents(%s) failed: %m", dest_dir);
+			return -1;
+		}
+		out_fd = open(temp_path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+	}
+	if (out_fd == -1) {
+		i_error("open(%s, O_CREAT) failed: %m", temp_path);
+		return -1;
+	}
+	output = o_stream_create_fd_file(out_fd, 0, FALSE);
+	i_stream_seek(file->input, 0);
+	while ((ret = o_stream_send_istream(output, file->input)) > 0) ;
+	if (ret == 0)
+		ret = o_stream_flush(output);
+	if (output->stream_errno != 0) {
+		errno = output->stream_errno;
+		i_error("write(%s) failed: %m", temp_path);
+		ret = -1;
+	} else if (file->input->stream_errno != 0) {
+		errno = file->input->stream_errno;
+		i_error("read(%s) failed: %m", file->current_path);
+		ret = -1;
+	} else if (ret < 0) {
+		i_error("o_stream_send_istream(%s, %s) "
+			"failed with unknown error",
+			temp_path, file->current_path);
+	}
+	o_stream_unref(&output);
+
+	if (!file->mbox->ibox.fsync_disable && ret == 0) {
+		if (fsync(out_fd) < 0) {
+			i_error("fsync(%s) failed: %m", temp_path);
+			ret = -1;
+		}
+	}
+	if (close(out_fd) < 0) {
+		i_error("close(%s) failed: %m", temp_path);
+		ret = -1;
+	}
+	if (ret < 0) {
+		(void)unlink(temp_path);
+		return -1;
+	}
+
+	/* the temp file was successfully written. rename it now to the
+	   destination file. the destination shouldn't exist, but if it does
+	   its contents should be the same (except for maybe older metadata) */
+	dest_path = t_strdup_printf("%s/%s", dest_dir, file->fname);
+	if (rename(temp_path, dest_path) < 0) {
+		i_error("rename(%s, %s) failed: %m", temp_path, dest_path);
+		(void)unlink(temp_path);
+		return -1;
+	}
+	if (!file->mbox->ibox.fsync_disable) {
+		if (fdatasync_path(dest_dir) < 0) {
+			i_error("fdatasync(%s) failed: %m", dest_dir);
+			(void)unlink(dest_path);
+			return -1;
+		}
+	}
+	if (unlink(file->current_path) < 0) {
+		i_error("unlink(%s) failed: %m", file->current_path);
+		if (errno == EACCES) {
+			/* configuration problem? revert the write */
+			(void)unlink(dest_path);
+		}
+		/* who knows what happened to the file. keep both just to be
+		   sure both won't get deleted. */
+		return -1;
+	}
+
+	/* file was successfully moved - reopen it */
+	dbox_file_close(file);
+	if (dbox_file_open(file, TRUE, &deleted) <= 0) {
+		i_error("dbox_file_move(%s): reopening file failed", dest_path);
+		return -1;
+	}
+	return 0;
+}
+
 void dbox_mail_metadata_flags_append(string_t *str, enum mail_flags flags)
 {
 	unsigned int i;
--- a/src/lib-storage/index/dbox/dbox-file.h	Tue Mar 04 04:05:35 2008 +0200
+++ b/src/lib-storage/index/dbox/dbox-file.h	Tue Mar 04 04:07:32 2008 +0200
@@ -128,6 +128,7 @@
 	/* Includes the trailing LF that shouldn't be used */
 	unsigned int metadata_len;
 
+	unsigned int alt_path:1;
 	unsigned int maildir_file:1;
 	unsigned int nonappendable:1;
 	unsigned int deleted:1;
@@ -217,6 +218,9 @@
 bool dbox_file_lookup(struct dbox_mailbox *mbox, struct mail_index_view *view,
 		      uint32_t seq, uint32_t *file_id_r, uoff_t *offset_r);
 
+/* Move the file to alt path or back. */
+int dbox_file_move(struct dbox_file *file, bool alt_path);
+
 /* Append flags as metadata value to given string */
 void dbox_mail_metadata_flags_append(string_t *str, enum mail_flags flags);
 /* Append keywords as metadata value to given string */
--- a/src/lib-storage/index/dbox/dbox-storage.c	Tue Mar 04 04:05:35 2008 +0200
+++ b/src/lib-storage/index/dbox/dbox-storage.c	Tue Mar 04 04:07:32 2008 +0200
@@ -151,16 +151,21 @@
 static const char *
 dbox_get_alt_path(struct dbox_storage *storage, const char *path)
 {
+	const char *root;
 	unsigned int len;
 
 	if (storage->alt_dir == NULL)
 		return NULL;
 
-	len = strlen(storage->alt_dir);
-	if (strncmp(path, storage->alt_dir, len) != 0)
-		return t_strconcat(storage->alt_dir, path + len, NULL);
-	else
+	root = mailbox_list_get_path(storage->storage.list, NULL,
+				     MAILBOX_LIST_PATH_TYPE_DIR);
+
+	len = strlen(root);
+	if (strncmp(path, root, len) != 0 && path[len] == '/') {
+		/* can't determine the alt path - shouldn't happen */
 		return NULL;
+	}
+	return t_strconcat(storage->alt_dir, path + len, NULL);
 }
 
 static struct mailbox *
--- a/src/lib-storage/index/dbox/dbox-storage.h	Tue Mar 04 04:05:35 2008 +0200
+++ b/src/lib-storage/index/dbox/dbox-storage.h	Tue Mar 04 04:07:32 2008 +0200
@@ -26,6 +26,9 @@
 #define DBOX_DEFAULT_ROTATE_DAYS 0
 #define DBOX_DEFAULT_MAX_OPEN_FILES 64
 
+/* Flag specifies if the message should be in primary or alternative storage */
+#define DBOX_INDEX_FLAG_ALT MAIL_INDEX_MAIL_FLAG_BACKEND
+
 struct dbox_index_header {
 	uint32_t last_dirty_flush_stamp;
 };
--- a/src/lib-storage/index/dbox/dbox-sync-file.c	Tue Mar 04 04:05:35 2008 +0200
+++ b/src/lib-storage/index/dbox/dbox-sync-file.c	Tue Mar 04 04:07:32 2008 +0200
@@ -324,9 +324,9 @@
 		first_expunge_seq = (uint32_t)-1;
 	}
 
-	if (array_is_created(&entry->changes))
+	if (array_is_created(&entry->changes)) {
 		seqs = array_get(&entry->changes, &count);
-	else {
+	} else {
 		seqs = NULL;
 		count = 0;
 	}
@@ -347,6 +347,31 @@
 }
 
 static void
+dbox_sync_file_move_if_needed(struct dbox_sync_context *ctx,
+			      struct dbox_file *file,
+			      const struct dbox_sync_file_entry *entry)
+{
+	const struct seq_range *seq;
+	const struct mail_index_record *rec;
+	bool new_alt_path;
+
+	if (!array_is_created(&entry->changes))
+		return;
+
+	/* check if we want to move the file to alt path or back.
+	   FIXME: change this check somehow when a file may contain
+	   multiple messages. */
+	seq = array_idx(&entry->changes, 0);
+	rec = mail_index_lookup(ctx->sync_view, seq[0].seq1);
+	new_alt_path = (rec->flags & DBOX_INDEX_FLAG_ALT) != 0;
+	if (new_alt_path != file->alt_path) {
+		/* move the file. if it fails, nothing broke so
+		   don't worry about it. */
+		(void)dbox_file_move(file, new_alt_path);
+	}
+}
+
+static void
 dbox_sync_mark_single_file_expunged(struct dbox_sync_context *ctx,
 				    const struct dbox_sync_file_entry *entry)
 {
@@ -394,8 +419,10 @@
 		}
 	} else {
 		ret = dbox_file_open_or_create(file, TRUE, &deleted);
-		if (ret > 0 && !deleted)
+		if (ret > 0 && !deleted) {
+			dbox_sync_file_move_if_needed(ctx, file, entry);
 			ret = dbox_sync_file_int(ctx, file, entry, locked);
+		}
 	}
 	dbox_file_unref(&file);
 	return ret;
--- a/src/lib-storage/index/dbox/dbox-sync.c	Tue Mar 04 04:05:35 2008 +0200
+++ b/src/lib-storage/index/dbox/dbox-sync.c	Tue Mar 04 04:07:32 2008 +0200
@@ -17,12 +17,13 @@
 #define DBOX_REBUILD_COUNT 3
 
 static int dbox_sync_add_seq(struct dbox_sync_context *ctx,
-			     enum mail_index_sync_type type, uint32_t seq)
+			     const struct mail_index_sync_rec *sync_rec,
+			     uint32_t seq)
 {
 	struct dbox_sync_file_entry *entry;
 	uint32_t file_id;
 	uoff_t offset;
-	bool uid_file;
+	bool uid_file, add;
 
 	if (!dbox_file_lookup(ctx->mbox, ctx->sync_view, seq,
 			      &file_id, &offset))
@@ -30,8 +31,22 @@
 
 	entry = hash_lookup(ctx->syncs, POINTER_CAST(file_id));
 	if (entry == NULL) {
-		if (type != MAIL_INDEX_SYNC_TYPE_EXPUNGE &&
-		    !ctx->flush_dirty_flags) {
+		if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE ||
+		    ctx->flush_dirty_flags) {
+			/* expunges / flushing dirty flags */
+			add = TRUE;
+		} else if (sync_rec->type != MAIL_INDEX_SYNC_TYPE_FLAGS) {
+			/* keywords, not flushing dirty flags */
+			add = FALSE;
+		} else {
+			/* add if we're moving from/to alternative storage
+			   and we actually have an alt directory specified */
+			add = ((sync_rec->add_flags | sync_rec->remove_flags) &
+			       DBOX_INDEX_FLAG_ALT) != 0 &&
+				ctx->mbox->alt_path != NULL;
+		}
+
+		if (!add) {
 			mail_index_update_flags(ctx->trans, seq, MODIFY_ADD,
 				(enum mail_flags)MAIL_INDEX_MAIL_FLAG_DIRTY);
 			return 0;
@@ -43,7 +58,7 @@
 	}
 	uid_file = (file_id & DBOX_FILE_ID_FLAG_UID) != 0;
 
-	if (type == MAIL_INDEX_SYNC_TYPE_EXPUNGE) {
+	if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE) {
 		if (!array_is_created(&entry->expunges)) {
 			p_array_init(&entry->expunges, ctx->pool,
 				     uid_file ? 1 : 3);
@@ -81,7 +96,7 @@
 	}
 
 	for (seq = seq1; seq <= seq2; seq++) {
-		if (dbox_sync_add_seq(ctx, sync_rec->type, seq) < 0)
+		if (dbox_sync_add_seq(ctx, sync_rec, seq) < 0)
 			return -1;
 	}
 	return 0;