Mercurial > dovecot > original-hg > dovecot-1.2
view src/lib-storage/index/cydir/cydir-save.c @ 6940:414c9d631a81 HEAD
Replaced t_push/t_pop calls with T_FRAME*() macros.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 05 Dec 2007 17:47:44 +0200 |
parents | 9ca7f055b646 |
children | 7ed926ed7aa4 |
line wrap: on
line source
/* Copyright (c) 2007 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "hostpid.h" #include "istream.h" #include "istream-crlf.h" #include "ostream.h" #include "str.h" #include "index-mail.h" #include "cydir-storage.h" #include "cydir-sync.h" #include <stdio.h> #include <utime.h> struct cydir_save_context { struct mail_save_context ctx; struct cydir_mailbox *mbox; struct mail_index_transaction *trans; char *tmp_basename; unsigned int mail_count; struct cydir_sync_context *sync_ctx; /* updated for each appended mail: */ uint32_t seq; struct istream *input; struct ostream *output; struct mail *mail, *cur_dest_mail; time_t cur_received_date; int fd; unsigned int failed:1; unsigned int finished:1; }; static char *cydir_generate_tmp_filename(void) { static unsigned int create_count = 0; return i_strdup_printf("temp.%s.P%sQ%uM%s.%s", dec2str(ioloop_timeval.tv_sec), my_pid, create_count++, dec2str(ioloop_timeval.tv_usec), my_hostname); } static const char * cydir_get_save_path(struct cydir_save_context *ctx, unsigned int num) { const char *dir; dir = mailbox_list_get_path(ctx->mbox->storage->storage.list, ctx->mbox->ibox.box.name, MAILBOX_LIST_PATH_TYPE_MAILBOX); return t_strdup_printf("%s/%s.%u", dir, ctx->tmp_basename, num); } int cydir_save_init(struct mailbox_transaction_context *_t, enum mail_flags flags, struct mail_keywords *keywords, time_t received_date, int timezone_offset ATTR_UNUSED, const char *from_envelope ATTR_UNUSED, struct istream *input, struct mail *dest_mail, struct mail_save_context **ctx_r) { struct cydir_transaction_context *t = (struct cydir_transaction_context *)_t; struct cydir_mailbox *mbox = (struct cydir_mailbox *)t->ictx.ibox; struct cydir_save_context *ctx = t->save_ctx; enum mail_flags save_flags; struct istream *crlf_input; i_assert((t->ictx.flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0); if (ctx == NULL) { ctx = t->save_ctx = i_new(struct cydir_save_context, 1); ctx->ctx.transaction = &t->ictx.mailbox_ctx; ctx->mbox = mbox; ctx->trans = t->ictx.trans; ctx->tmp_basename = cydir_generate_tmp_filename(); } T_FRAME( const char *path; path = cydir_get_save_path(ctx, ctx->mail_count); ctx->fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0660); if (ctx->fd != -1) { ctx->output = o_stream_create_fd_file(ctx->fd, 0, FALSE); o_stream_cork(ctx->output); } else { mail_storage_set_critical(_t->box->storage, "open(%s) failed: %m", path); ctx->failed = TRUE; } ); if (ctx->failed) return -1; /* add to index */ save_flags = flags & ~MAIL_RECENT; mail_index_append(ctx->trans, 0, &ctx->seq); mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE, save_flags); if (keywords != NULL) { mail_index_update_keywords(ctx->trans, ctx->seq, MODIFY_REPLACE, keywords); } if (dest_mail == NULL) { if (ctx->mail == NULL) ctx->mail = mail_alloc(_t, 0, NULL); dest_mail = ctx->mail; } mail_set_seq(dest_mail, ctx->seq); crlf_input = i_stream_create_crlf(input); ctx->input = index_mail_cache_parse_init(dest_mail, crlf_input); i_stream_unref(&crlf_input); ctx->cur_dest_mail = dest_mail; ctx->cur_received_date = received_date; *ctx_r = &ctx->ctx; return ctx->failed ? -1 : 0; } int cydir_save_continue(struct mail_save_context *_ctx) { struct cydir_save_context *ctx = (struct cydir_save_context *)_ctx; struct mail_storage *storage = &ctx->mbox->storage->storage; if (ctx->failed) return -1; do { if (o_stream_send_istream(ctx->output, ctx->input) < 0) { if (!mail_storage_set_error_from_errno(storage)) { mail_storage_set_critical(storage, "o_stream_send_istream(%s) failed: %m", cydir_get_save_path(ctx, ctx->mail_count)); } ctx->failed = TRUE; return -1; } index_mail_cache_parse_continue(ctx->cur_dest_mail); /* both tee input readers may consume data from our primary input stream. we'll have to make sure we don't return with one of the streams still having data in them. */ } while (i_stream_read(ctx->input) > 0); return 0; } int cydir_save_finish(struct mail_save_context *_ctx) { struct cydir_save_context *ctx = (struct cydir_save_context *)_ctx; struct mail_storage *storage = &ctx->mbox->storage->storage; const char *path = cydir_get_save_path(ctx, ctx->mail_count); struct stat st; ctx->finished = TRUE; if (o_stream_flush(ctx->output) < 0) { mail_storage_set_critical(storage, "o_stream_flush(%s) failed: %m", path); ctx->failed = TRUE; } if (!ctx->mbox->ibox.fsync_disable) { if (fsync(ctx->fd) < 0) { mail_storage_set_critical(storage, "fsync(%s) failed: %m", path); ctx->failed = TRUE; } } if (ctx->cur_received_date == (time_t)-1) { if (fstat(ctx->fd, &st) == 0) ctx->cur_received_date = st.st_mtime; else { mail_storage_set_critical(storage, "fstat(%s) failed: %m", path); ctx->failed = TRUE; } } else { struct utimbuf ut; ut.actime = ioloop_time; ut.modtime = ctx->cur_received_date; if (utime(path, &ut) < 0) { mail_storage_set_critical(storage, "utime(%s) failed: %m", path); ctx->failed = TRUE; } } o_stream_destroy(&ctx->output); if (close(ctx->fd) < 0) { mail_storage_set_critical(storage, "close(%s) failed: %m", path); ctx->failed = TRUE; } ctx->fd = -1; if (!ctx->failed) ctx->mail_count++; else { if (unlink(path) < 0) { mail_storage_set_critical(storage, "unlink(%s) failed: %m", path); } } index_mail_cache_parse_deinit(ctx->cur_dest_mail, ctx->cur_received_date); i_stream_unref(&ctx->input); return ctx->failed ? -1 : 0; } void cydir_save_cancel(struct mail_save_context *_ctx) { struct cydir_save_context *ctx = (struct cydir_save_context *)_ctx; ctx->failed = TRUE; (void)cydir_save_finish(_ctx); } int cydir_transaction_save_commit_pre(struct cydir_save_context *ctx) { struct cydir_transaction_context *t = (struct cydir_transaction_context *)ctx->ctx.transaction; const struct mail_index_header *hdr; uint32_t i, uid, next_uid; const char *dir; string_t *src_path, *dest_path; unsigned int src_prefixlen, dest_prefixlen; i_assert(ctx->finished); if (cydir_sync_begin(ctx->mbox, &ctx->sync_ctx, TRUE) < 0) { ctx->failed = TRUE; cydir_transaction_save_rollback(ctx); return -1; } hdr = mail_index_get_header(ctx->sync_ctx->sync_view); uid = hdr->next_uid; mail_index_append_assign_uids(ctx->trans, uid, &next_uid); *t->ictx.saved_uid_validity = ctx->sync_ctx->uid_validity; *t->ictx.first_saved_uid = uid; *t->ictx.last_saved_uid = next_uid - 1; dir = mailbox_list_get_path(ctx->mbox->storage->storage.list, ctx->mbox->ibox.box.name, MAILBOX_LIST_PATH_TYPE_MAILBOX); src_path = t_str_new(256); str_printfa(src_path, "%s/%s.", dir, ctx->tmp_basename); src_prefixlen = str_len(src_path); dest_path = t_str_new(256); str_append(dest_path, dir); str_append_c(dest_path, '/'); dest_prefixlen = str_len(dest_path); for (i = 0; i < ctx->mail_count; i++, uid++) { str_truncate(src_path, src_prefixlen); str_truncate(dest_path, dest_prefixlen); str_printfa(src_path, "%u", i); str_printfa(dest_path, "%u.", uid); if (rename(str_c(src_path), str_c(dest_path)) < 0) { mail_storage_set_critical(&ctx->mbox->storage->storage, "rename(%s, %s) failed: %m", str_c(src_path), str_c(dest_path)); ctx->failed = TRUE; cydir_transaction_save_rollback(ctx); return -1; } } if (ctx->mail != NULL) mail_free(&ctx->mail); return 0; } void cydir_transaction_save_commit_post(struct cydir_save_context *ctx) { ctx->ctx.transaction = NULL; /* transaction is already freed */ (void)cydir_sync_finish(&ctx->sync_ctx, TRUE); cydir_transaction_save_rollback(ctx); } void cydir_transaction_save_rollback(struct cydir_save_context *ctx) { if (!ctx->finished) cydir_save_cancel(&ctx->ctx); if (ctx->sync_ctx != NULL) (void)cydir_sync_finish(&ctx->sync_ctx, FALSE); if (ctx->mail != NULL) mail_free(&ctx->mail); i_free(ctx->tmp_basename); i_free(ctx); }