maildir-save.c revision c7a50b2c29780eaa3668bbac738e3fa3e4e171e8
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2002-2011 Dovecot authors, see the included COPYING file */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* unsigned int keywords[]; */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct maildir_uidlist_sync_ctx *uidlist_sync_ctx;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct maildir_keywords_sync_ctx *keywords_sync_ctx;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct maildir_filename *files, **files_tail, *file_last;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi unsigned int files_count;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int maildir_file_move(struct maildir_save_context *ctx,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct maildir_filename *mf, const char *destname,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_storage *storage = &ctx->mbox->storage->storage;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* if we have flags, we'll move it to cur/ directly, because files in
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi new/ directory can't have flags. alternative would be to write it
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi in new/ and set the flags dirty in index file, but in that case
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi external MUAs would see wrong flags. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi tmp_path = t_strconcat(ctx->tmpdir, "/", mf->tmp_name, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* maildir spec says we should use link() + unlink() here. however
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi since our filename is guaranteed to be unique, rename() works just
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi as well, except faster. even if the filename wasn't unique, the
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi problem could still happen if the file was already moved from
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi new/ to cur/, so link() doesn't really provide any safety anyway.
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi Besides the small temporary performance benefits, this rename() is
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi almost required with OSX's HFS+ filesystem, since it implements
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi hard links in a pretty ugly way, which makes the performance crawl
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi when a lot of hard links are used. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_set_error(storage, MAIL_ERROR_NOSPACE,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomimaildir_save_transaction_init(struct mailbox_transaction_context *t)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct maildir_mailbox *mbox = (struct maildir_mailbox *)t->box;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi pool = pool_alloconly_create("maildir_save_context", 4096);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx = p_new(pool, struct maildir_save_context, 1);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->tmpdir = p_strconcat(pool, mbox->box.path, "/tmp", NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->newdir = p_strconcat(pool, mbox->box.path, "/new", NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->curdir = p_strconcat(pool, mbox->box.path, "/cur", NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi buffer_create_const_data(&ctx->keywords_buffer, NULL, 0);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi array_create_from_buffer(&ctx->keywords_array, &ctx->keywords_buffer,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi sizeof(unsigned int));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomimaildir_save_add(struct mail_save_context *_ctx, const char *tmp_fname,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* allow caller to specify recent flag only when uid is specified
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi (we're replicating, converting, etc.). */
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch /* now, we want to be able to rollback the whole append session,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi so we'll just store the name of this temp file and move it later
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi into new/ or cur/. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* @UNSAFE */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi keyword_count = _ctx->keywords == NULL ? 0 : _ctx->keywords->count;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi sizeof(unsigned int) * keyword_count);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mf->tmp_name = mf->dest_basename = p_strdup(ctx->pool, tmp_fname);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* @UNSAFE */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi sizeof(unsigned int) * keyword_count);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mf->pop3_uidl = p_strdup(ctx->pool, _ctx->pop3_uidl);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* insert into index */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mail_index_append(ctx->trans, _ctx->uid, &ctx->seq);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->mail = mail_alloc(_ctx->transaction, 0, NULL);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* copying with hardlinking. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi index_copy_cache_fields(_ctx, src_mail, ctx->seq);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi input = index_mail_cache_parse_init(_ctx->dest_mail,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomivoid maildir_save_set_dest_basename(struct mail_save_context *_ctx,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi mf->dest_basename = p_strdup(ctx->pool, basename);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomivoid maildir_save_set_sizes(struct maildir_filename *mf,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomimaildir_get_dest_filename(struct maildir_save_context *ctx,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char **fname_r)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (mf->size != (uoff_t)-1 && !mf->preserve_filename) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi basename = t_strdup_printf("%s,%c=%"PRIuUOFF_T, basename,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (mf->vsize != (uoff_t)-1 && !mf->preserve_filename) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi basename = t_strdup_printf("%s,%c=%"PRIuUOFF_T, basename,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if ((mf->flags & MAIL_FLAGS_MASK) == MAIL_RECENT) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *fname_r = maildir_filename_set_flags(NULL, basename,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_assert(ctx->keywords_sync_ctx != NULL || mf->keywords_count == 0);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi buffer_create_const_data(&ctx->keywords_buffer, mf + 1,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi *fname_r = maildir_filename_set_flags(ctx->keywords_sync_ctx, basename,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic const char *maildir_mf_get_path(struct maildir_save_context *ctx,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if ((mf->flags & MAILDIR_FILENAME_FLAG_MOVED) == 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* file is still in tmp/ */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return t_strdup_printf("%s/%s", ctx->tmpdir, mf->tmp_name);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* already moved to new/ or cur/ */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi dir = maildir_get_dest_filename(ctx, mf, &fname) ?
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiconst char *maildir_save_file_get_path(struct mailbox_transaction_context *t,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi while (seq > 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int maildir_create_tmp(struct maildir_mailbox *mbox, const char *dir,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char **fname_r)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi unsigned int prefix_len;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* the generated filename is unique. the only reason why it
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi might return an existing filename is if the time moved
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi backwards. so we'll use O_EXCL anyway, although it's mostly
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomimaildir_save_alloc(struct mailbox_transaction_context *t)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiint maildir_save_begin(struct mail_save_context *_ctx, struct istream *input)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* new mail, new failure state */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* create a new file in tmp/ directory */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const char *fname;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ctx->fd = maildir_create_tmp(ctx->mbox, ctx->tmpdir, &fname);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (ctx->mbox->storage->storage.set->mail_save_crlf)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi _ctx->output = o_stream_create_fd_file(ctx->fd, 0, FALSE);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiint maildir_save_continue(struct mail_save_context *_ctx)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi struct mail_storage *storage = &ctx->mbox->storage->storage;
const char *path)
struct index_transaction_context *t =
const char *path;
int output_errno;
} else if (errno != 0) {
int ret;
T_BEGIN {
} T_END;
return ret;
} T_END;
if (new_changed) {
if (cur_changed) {
static uint32_t
unsigned int i, count;
if (count == 0)
for (i = 0; i < count; i++) {
int ret;
if (ret == 0) {
T_BEGIN {
} T_END;
first_recent_uid = 0;
if (first_recent_uid != 0) {
struct index_transaction_context *t =
return TRUE;
return TRUE;
return FALSE;
&size))
int ret;
T_BEGIN {
const char *dest;
prev_mf);
if (newdir)
} T_END;
if (ret < 0)
int ret;
const char *dest;
if (newdir)
} T_END;
int ret;
if (ret > 0) {
} else if (ret == 0 &&
T_BEGIN {
} T_END;
if (ret == 0) {
ret == 0) < 0)
if (ret < 0)
if (ret < 0) {