Mercurial > dovecot > original-hg > dovecot-1.2
view src/lib-storage/index/dbox/dbox-sync-file.c @ 9532:00cd9aacd03c HEAD
Updated copyright notices to include year 2010.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 25 Jan 2010 01:18:58 +0200 |
parents | e579406fdab6 |
children |
line wrap: on
line source
/* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "array.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "dbox-storage.h" #include "dbox-index.h" #include "dbox-file.h" #include "dbox-sync.h" static int dbox_sync_file_unlink(struct dbox_file *file) { const char *path; int i; path = t_strdup_printf("%s/%s", file->mbox->path, file->fname); for (i = 0;; i++) { if (unlink(path) == 0) break; if (errno != ENOENT) { mail_storage_set_critical(file->mbox->ibox.box.storage, "unlink(%s) failed: %m", path); return -1; } if (file->mbox->alt_path == NULL || i == 1) { /* not found */ i_warning("dbox: File unexpectedly lost: %s/%s", file->mbox->path, file->fname); break; } /* try the alternative path */ path = t_strdup_printf("%s/%s", file->mbox->alt_path, file->fname); } return 0; } static void dbox_sync_update_metadata(struct dbox_sync_context *ctx, struct dbox_file *file, const struct dbox_sync_file_entry *entry, uint32_t seq) { const struct mail_index_record *rec; ARRAY_TYPE(keyword_indexes) keyword_indexes; struct mail_keywords *keywords; string_t *value; const char *old_value; value = t_str_new(256); /* flags */ rec = mail_index_lookup(ctx->sync_view, seq); dbox_mail_metadata_flags_append(value, rec->flags); dbox_file_metadata_set(file, DBOX_METADATA_FLAGS, str_c(value)); /* keywords */ t_array_init(&keyword_indexes, 32); mail_index_lookup_keywords(ctx->sync_view, seq, &keyword_indexes); old_value = dbox_file_metadata_get(file, DBOX_METADATA_KEYWORDS); if (array_count(&keyword_indexes) > 0 || (old_value != NULL && *old_value != '\0' && array_count(&keyword_indexes) == 0)) { str_truncate(value, 0); keywords = mail_index_keywords_create_from_indexes( ctx->mbox->ibox.index, &keyword_indexes); dbox_mail_metadata_keywords_append(ctx->mbox, value, keywords); mail_index_keywords_free(&keywords); dbox_file_metadata_set(file, DBOX_METADATA_KEYWORDS, str_c(value)); } /* expunge state */ if (entry != NULL && array_is_created(&entry->expunges) && seq_range_exists(&entry->expunges, seq)) { dbox_file_metadata_set(file, DBOX_METADATA_EXPUNGED, "1"); mail_index_expunge(ctx->trans, seq); } } static int dbox_sync_file_expunge(struct dbox_sync_context *ctx, struct dbox_file *file, const struct dbox_sync_file_entry *entry) { const struct seq_range *expunges; struct dbox_file *out_file = NULL; struct istream *input; struct ostream *output; uint32_t file_id, seq, uid; uoff_t first_offset, offset, physical_size; const char *out_path; unsigned int i, count; bool expunged; int ret; expunges = array_get(&entry->expunges, &count); if (!dbox_file_lookup(ctx->mbox, ctx->sync_view, expunges[0].seq1, &file_id, &first_offset)) return 0; i_assert(file_id == file->file_id); mail_index_expunge(ctx->trans, expunges[0].seq1); offset = first_offset; for (i = 0;;) { if ((ret = dbox_file_seek_next(file, &offset, &uid, &physical_size)) <= 0) break; if (uid == 0) { /* EOF */ break; } if (i < count) { mail_index_lookup_seq(ctx->sync_view, uid, &seq); while (seq > expunges[i].seq2) { if (++i == count) break; } } if (seq == 0 || (i < count && seq >= expunges[i].seq1 && seq <= expunges[i].seq2)) { /* this message gets expunged */ if (seq != 0) mail_index_expunge(ctx->trans, seq); continue; } /* non-expunged message. write it to output file. */ if (out_file == NULL) { out_file = dbox_file_init(ctx->mbox, 0); ret = dbox_file_get_append_stream(out_file, physical_size, &output); if (ret <= 0) break; } i_stream_seek(file->input, offset); input = i_stream_create_limit(file->input, file->msg_header_size + physical_size); ret = o_stream_send_istream(output, input) < 0 ? -1 : 0; i_stream_unref(&input); if (ret < 0) break; /* write metadata */ (void)dbox_file_metadata_seek_mail_offset(file, offset, &expunged); T_BEGIN { dbox_sync_update_metadata(ctx, file, entry, seq); } T_END; if ((ret = dbox_file_metadata_write_to(file, output)) < 0) break; mail_index_update_flags(ctx->trans, seq, MODIFY_REMOVE, (enum mail_flags)MAIL_INDEX_MAIL_FLAG_DIRTY); } out_path = out_file == NULL ? NULL : dbox_file_get_path(out_file); if (ret <= 0) { if (out_file != NULL) { if (unlink(out_path) < 0) i_error("unlink(%s) failed: %m", out_path); o_stream_unref(&output); } } else if (out_file != NULL) { /* FIXME: rename out_file and add to index */ o_stream_unref(&output); } if (ret <= 0) ; else if (first_offset == file->file_header_size) { /* nothing exists in this file anymore */ ret = dbox_sync_file_unlink(file); } else { if (ftruncate(file->fd, first_offset) < 0) { dbox_file_set_syscall_error(file, "ftruncate"); ret = -1; } } if (out_file != NULL) dbox_file_unref(&out_file); return ret; } static int dbox_sync_file_split(struct dbox_sync_context *ctx, struct dbox_file *in_file, uoff_t offset, uint32_t seq) { static enum dbox_metadata_key maildir_metadata_keys[] = { DBOX_METADATA_VIRTUAL_SIZE, DBOX_METADATA_RECEIVED_TIME, DBOX_METADATA_SAVE_TIME, DBOX_METADATA_POP3_UIDL }; struct dbox_index_append_context *append_ctx; struct dbox_file *out_file; struct istream *input; struct ostream *output; struct dbox_message_header dbox_msg_hdr; struct dbox_mail_index_record rec; const char *out_path, *value; uint32_t uid; uoff_t size, append_offset; unsigned int i; int ret; bool expunged; /* FIXME: for now we handle only maildir file conversion */ i_assert(in_file->maildir_file); ret = dbox_file_get_mail_stream(in_file, offset, &uid, &size, &input, &expunged); if (ret <= 0) return ret; if (expunged) { mail_index_expunge(ctx->trans, seq); return 1; } append_ctx = dbox_index_append_begin(ctx->mbox->dbox_index); if (dbox_index_append_next(append_ctx, size, &out_file, &output) < 0 || dbox_file_metadata_seek(out_file, 0, &expunged) < 0) { dbox_index_append_rollback(&append_ctx); i_stream_unref(&input); return -1; } append_offset = output->offset; dbox_msg_header_fill(&dbox_msg_hdr, uid, size); T_BEGIN { dbox_sync_update_metadata(ctx, out_file, NULL, seq); } T_END; /* set static metadata */ for (i = 0; i < N_ELEMENTS(maildir_metadata_keys); i++) { value = dbox_file_metadata_get(in_file, maildir_metadata_keys[i]); if (value != NULL) { dbox_file_metadata_set(out_file, maildir_metadata_keys[i], value); } } /* copy the message */ out_path = dbox_file_get_path(out_file); o_stream_cork(output); if (o_stream_send(output, &dbox_msg_hdr, sizeof(dbox_msg_hdr)) < 0 || o_stream_send_istream(output, input) < 0 || dbox_file_metadata_write_to(out_file, output) < 0 || o_stream_flush(output) < 0) { mail_storage_set_critical(&ctx->mbox->storage->storage, "write(%s) failed: %m", out_path); ret = -1; } else { dbox_file_finish_append(out_file); out_file->last_append_uid = uid; ret = dbox_index_append_assign_file_ids(append_ctx); } if (ret < 0) dbox_index_append_rollback(&append_ctx); else ret = dbox_index_append_commit(&append_ctx); i_stream_unref(&input); if (ret == 0) { /* update message position in index file */ memset(&rec, 0, sizeof(rec)); if ((rec.file_id & DBOX_FILE_ID_FLAG_UID) == 0) { rec.file_id = out_file->file_id; rec.offset = append_offset; } mail_index_update_ext(ctx->trans, seq, ctx->mbox->dbox_ext_id, &rec, NULL); /* when everything is done, unlink the old file */ ret = dbox_sync_file_unlink(in_file); } return ret < 0 ? -1 : 1; } static int dbox_sync_file_changes(struct dbox_sync_context *ctx, struct dbox_file *file, const struct dbox_sync_file_entry *entry, uint32_t seq) { uint32_t file_id; uoff_t offset; bool expunged; int ret; if (!dbox_file_lookup(ctx->mbox, ctx->sync_view, seq, &file_id, &offset)) return 0; i_assert(file_id == file->file_id); ret = dbox_file_metadata_seek_mail_offset(file, offset, &expunged); if (ret <= 0) return ret; if (expunged) { mail_index_expunge(ctx->trans, seq); return 1; } T_BEGIN { dbox_sync_update_metadata(ctx, file, entry, seq); } T_END; ret = dbox_file_metadata_write(file); if (ret <= 0) { return ret < 0 ? -1 : dbox_sync_file_split(ctx, file, offset, seq); } mail_index_update_flags(ctx->trans, seq, MODIFY_REMOVE, (enum mail_flags)MAIL_INDEX_MAIL_FLAG_DIRTY); return 1; } static int dbox_sync_file_int(struct dbox_sync_context *ctx, struct dbox_file *file, const struct dbox_sync_file_entry *entry, bool full_expunge) { const struct seq_range *seqs; unsigned int i, count; uint32_t seq, first_expunge_seq; int ret; if (array_is_created(&entry->expunges) && full_expunge) { seqs = array_idx(&entry->expunges, 0); first_expunge_seq = seqs->seq1; } else { first_expunge_seq = (uint32_t)-1; } if (array_is_created(&entry->changes)) { seqs = array_get(&entry->changes, &count); } else { seqs = NULL; count = 0; } for (i = 0; i < count; ) { for (seq = seqs[i].seq1; seq <= seqs[i].seq2; seq++) { if (seq >= first_expunge_seq) return dbox_sync_file_expunge(ctx, file, entry); ret = dbox_sync_file_changes(ctx, file, entry, seq); if (ret <= 0) return ret; } i++; } if (first_expunge_seq != (uint32_t)-1) return dbox_sync_file_expunge(ctx, file, entry); return 1; } 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) { struct mailbox *box = &ctx->mbox->ibox.box; const struct seq_range *expunges; unsigned int count; uint32_t uid; expunges = array_get(&entry->expunges, &count); i_assert(count == 1 && expunges[0].seq1 == expunges[0].seq2); mail_index_expunge(ctx->trans, expunges[0].seq1); if (box->v.sync_notify != NULL) { mail_index_lookup_uid(ctx->sync_view, expunges[0].seq1, &uid); box->v.sync_notify(box, uid, MAILBOX_SYNC_TYPE_EXPUNGE); } } int dbox_sync_file(struct dbox_sync_context *ctx, const struct dbox_sync_file_entry *entry) { struct dbox_file *file; struct dbox_index_record *rec; enum dbox_index_file_status status; bool locked, deleted; int ret; if ((entry->file_id & DBOX_FILE_ID_FLAG_UID) != 0) { locked = TRUE; status = DBOX_INDEX_FILE_STATUS_SINGLE_MESSAGE; } else { rec = dbox_index_record_lookup(ctx->mbox->dbox_index, entry->file_id); if (rec == NULL || rec->status == DBOX_INDEX_FILE_STATUS_UNLINKED) { /* file doesn't exist, nothing to do */ return 1; } locked = rec->locked; status = rec->status; } file = dbox_file_init(ctx->mbox, entry->file_id); if ((status == DBOX_INDEX_FILE_STATUS_SINGLE_MESSAGE || status == DBOX_INDEX_FILE_STATUS_MAILDIR) && array_is_created(&entry->expunges)) { /* fast path to expunging the whole file */ if (dbox_sync_file_unlink(file) < 0) ret = -1; else { dbox_sync_mark_single_file_expunged(ctx, entry); ret = 1; } } else { ret = dbox_file_open_or_create(file, TRUE, &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; }