# HG changeset patch # User Timo Sirainen # Date 1204596452 -7200 # Node ID 9ef06104648ae6f25e6a6c733c79ef0fc261a340 # Parent 93f67b71476c60a7c9858e98ffecb5ab40e021ef 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. diff -r 93f67b71476c -r 9ef06104648a src/lib-storage/index/dbox/dbox-file.c --- 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; diff -r 93f67b71476c -r 9ef06104648a src/lib-storage/index/dbox/dbox-file.h --- 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 */ diff -r 93f67b71476c -r 9ef06104648a src/lib-storage/index/dbox/dbox-storage.c --- 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 * diff -r 93f67b71476c -r 9ef06104648a src/lib-storage/index/dbox/dbox-storage.h --- 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; }; diff -r 93f67b71476c -r 9ef06104648a src/lib-storage/index/dbox/dbox-sync-file.c --- 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; diff -r 93f67b71476c -r 9ef06104648a src/lib-storage/index/dbox/dbox-sync.c --- 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;