maildir-save.c revision 28ec56f48d67d0a43f77f9b16b5005418a404bbf
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
fd3d711f219fd6813492acbe051e04327f0ca0f0Timo Sirainen /* unsigned int keywords[]; */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct maildir_uidlist_sync_ctx *uidlist_sync_ctx;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct maildir_keywords_sync_ctx *keywords_sync_ctx;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen struct maildir_filename *files, **files_tail, *file_last;
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainenstatic int maildir_file_move(struct maildir_save_context *ctx,
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen struct maildir_filename *mf, const char *destname,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_storage *storage = &ctx->mbox->storage->storage;
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen /* if we have flags, we'll move it to cur/ directly, because files in
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen new/ directory can't have flags. alternative would be to write it
51795bfe9d05d92fe942cb451aec2b9d16d32a11Timo Sirainen in new/ and set the flags dirty in index file, but in that case
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen external MUAs would see wrong flags. */
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen tmp_path = t_strconcat(ctx->tmpdir, "/", mf->basename, NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_strconcat(ctx->newdir, "/", destname, NULL) :
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen t_strconcat(ctx->curdir, "/", destname, NULL);
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen /* maildir spec says we should use link() + unlink() here. however
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen since our filename is guaranteed to be unique, rename() works just
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen as well, except faster. even if the filename wasn't unique, the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen problem could still happen if the file was already moved from
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen new/ to cur/, so link() doesn't really provide any safety anyway.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen Besides the small temporary performance benefits, this rename() is
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen almost required with OSX's HFS+ filesystem, since it implements
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen hard links in a pretty ugly way, which makes the performance crawl
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen when a lot of hard links are used. */
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen mail_storage_set_error(storage, MAIL_ERROR_NOSPACE,
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
c8adec8db635f5efb13b9879a5f3fb523abdc969Timo Sirainenmaildir_save_transaction_init(struct mailbox_transaction_context *t)
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen struct maildir_mailbox *mbox = (struct maildir_mailbox *)t->box;
26ff8f8a4867bf8e9551a27a2de8c12cd138b065Timo Sirainen pool = pool_alloconly_create("maildir_save_context", 4096);
26ff8f8a4867bf8e9551a27a2de8c12cd138b065Timo Sirainen ctx = p_new(pool, struct maildir_save_context, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ctx->trans = ((struct index_transaction_context *)t)->trans;
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen ctx->tmpdir = p_strconcat(pool, mbox->ibox.box.path, "/tmp", NULL);
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen ctx->newdir = p_strconcat(pool, mbox->ibox.box.path, "/new", NULL);
c8adec8db635f5efb13b9879a5f3fb523abdc969Timo Sirainen ctx->curdir = p_strconcat(pool, mbox->ibox.box.path, "/cur", NULL);
c8adec8db635f5efb13b9879a5f3fb523abdc969Timo Sirainen buffer_create_const_data(&ctx->keywords_buffer, NULL, 0);
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen array_create_from_buffer(&ctx->keywords_array, &ctx->keywords_buffer,
2d39dc1a453546892109b35c0d9770369011a13dTimo Sirainen sizeof(unsigned int));
62f4a199b5c9a0862f486cbf18e195cc621bbe25Timo Sirainenmaildir_save_add(struct mail_save_context *_ctx, const char *base_fname)
6bc98d3898c475ba7615ba2b016e5142c8b2c09fTimo Sirainen struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* allow caller to specify recent flag only when uid is specified
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (we're replicating, converting, etc.). */
020a39a395d2adb768e0179631b37bc78ecd9471Timo Sirainen /* now, we want to be able to rollback the whole append session,
020a39a395d2adb768e0179631b37bc78ecd9471Timo Sirainen so we'll just store the name of this temp file and move it later
020a39a395d2adb768e0179631b37bc78ecd9471Timo Sirainen into new/ or cur/. */
020a39a395d2adb768e0179631b37bc78ecd9471Timo Sirainen /* @UNSAFE */
020a39a395d2adb768e0179631b37bc78ecd9471Timo Sirainen keyword_count = _ctx->keywords == NULL ? 0 : _ctx->keywords->count;
020a39a395d2adb768e0179631b37bc78ecd9471Timo Sirainen sizeof(unsigned int) * keyword_count);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen mf->basename = p_strdup(ctx->pool, base_fname);
20a802016205bbcafc90f164f769ea801f88d014Timo Sirainen /* @UNSAFE */
a53cb86b4d733d9c48ee4d285bed477c80825804Timo Sirainen sizeof(unsigned int) * keyword_count);
a045c3aba2610c6ed0bf1c346df1c6d8f7b9fbfdTimo Sirainen /* insert into index */
a045c3aba2610c6ed0bf1c346df1c6d8f7b9fbfdTimo Sirainen mail_index_append(ctx->trans, _ctx->uid, &ctx->seq);
d30da25fb6be1f1c667d93767c9194000194b618Timo Sirainen mail_index_update_keywords(ctx->trans, ctx->seq,
d30da25fb6be1f1c667d93767c9194000194b618Timo Sirainen mail_index_update_modseq(ctx->trans, ctx->seq,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ctx->mail = mail_alloc(_ctx->transaction, 0, NULL);
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen /* FIXME: copying with hardlinking. we could copy the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cached data directly */
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen input = index_mail_cache_parse_init(_ctx->dest_mail,
e376693bfa3985232c41df99c7010fca22612c89Timo Sirainenmaildir_get_updated_filename(struct maildir_save_context *ctx,
e376693bfa3985232c41df99c7010fca22612c89Timo Sirainen const char **fname_r)
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen if (ctx->mbox->storage->save_size_in_filename &&
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen mf->size != (uoff_t)-1 && !mf->preserve_filename) {
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen basename = t_strdup_printf("%s,%c=%"PRIuUOFF_T, basename,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (mf->vsize != (uoff_t)-1 && !mf->preserve_filename) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen basename = t_strdup_printf("%s,%c=%"PRIuUOFF_T, basename,
d152ccd0d29fae1bc6092bf198ee7eb843202f96Timo Sirainen if ((mf->flags & MAIL_FLAGS_MASK) == MAIL_RECENT) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *fname_r = maildir_filename_set_flags(NULL, basename,
d152ccd0d29fae1bc6092bf198ee7eb843202f96Timo Sirainen i_assert(ctx->keywords_sync_ctx != NULL || mf->keywords_count == 0);
d152ccd0d29fae1bc6092bf198ee7eb843202f96Timo Sirainen buffer_create_const_data(&ctx->keywords_buffer, mf + 1,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *fname_r = maildir_filename_set_flags(ctx->keywords_sync_ctx, basename,
8f8315e4b4e27ead12dd1c3da65bf4dee3762f18Timo Sirainenstatic const char *maildir_mf_get_path(struct maildir_save_context *ctx,
8f8315e4b4e27ead12dd1c3da65bf4dee3762f18Timo Sirainen if ((mf->flags & MAILDIR_FILENAME_FLAG_MOVED) == 0) {
8f8315e4b4e27ead12dd1c3da65bf4dee3762f18Timo Sirainen /* file is still in tmp/ */
8f8315e4b4e27ead12dd1c3da65bf4dee3762f18Timo Sirainen return t_strdup_printf("%s/%s", ctx->tmpdir, mf->basename);
768b7f5783c8de119d7386321e5d0c72d5c2d9f6Timo Sirainen /* already moved to new/ or cur/ */
768b7f5783c8de119d7386321e5d0c72d5c2d9f6Timo Sirainen if (maildir_get_updated_filename(ctx, mf, &fname))
768b7f5783c8de119d7386321e5d0c72d5c2d9f6Timo Sirainen return t_strdup_printf("%s/%s", ctx->newdir, mf->basename);
b08b33d1f5ce3721dc2d83586c9cb0ca141331fdTimo Sirainen return t_strdup_printf("%s/%s", ctx->curdir, fname);
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenconst char *maildir_save_file_get_path(struct mailbox_transaction_context *t,
87712707722ef7d73acb065546e61afa4455cd9eTimo Sirainen while (seq > 0) {
7761758f43d6150be4b07f4c54457ce662f78c4cTimo Sirainenstatic int maildir_create_tmp(struct maildir_mailbox *mbox, const char *dir,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char **fname)
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen /* stat() first to see if it exists. pretty much the only
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen possibility of that happening is if time had moved
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen backwards, but even then it's highly unlikely. */
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen /* try another file name */
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen /* doesn't exist */
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen mode_t old_mask = umask(0777 & ~box->file_create_mode);
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen /* race condition between stat() and open().
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen highly unlikely. */
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen } else if (box->file_create_gid != (gid_t)-1) {
c251a38df327599a62d341bf5c2282f31352faa5Timo Sirainen if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainenmaildir_save_alloc(struct mailbox_transaction_context *t)
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen i_assert((t->flags & MAILBOX_TRANSACTION_FLAG_EXTERNAL) != 0);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen t->save_ctx = maildir_save_transaction_init(t);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainenint maildir_save_begin(struct mail_save_context *_ctx, struct istream *input)
61f5256ef248d35459b53534ae428bf6d016e1c5Timo Sirainen struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
41e1c7380edda701719d8ce1fb4d465d2ec4c84dTimo Sirainen /* create a new file in tmp/ directory */
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen ctx->fd = maildir_create_tmp(ctx->mbox, ctx->tmpdir, &fname);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ctx->mbox->storage->storage.set->mail_save_crlf)
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen _ctx->output = o_stream_create_fd_file(ctx->fd, 0, FALSE);
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainenint maildir_save_continue(struct mail_save_context *_ctx)
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen struct mail_storage *storage = &ctx->mbox->storage->storage;
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen if (o_stream_send_istream(_ctx->output, ctx->input) < 0) {
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (!mail_storage_set_error_from_errno(storage)) {
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen "o_stream_send_istream(%s/%s) "
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen "failed: %m",
5278c93bd7105c32ac7ec37f36015d5950f6cbcaTimo Sirainen index_mail_cache_parse_continue(ctx->cur_dest_mail);
44c5e644cb413a6559bf2d4179cbe48f9a82f366Timo Sirainen /* both tee input readers may consume data from our primary
5278c93bd7105c32ac7ec37f36015d5950f6cbcaTimo Sirainen input stream. we'll have to make sure we don't return with
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen one of the streams still having data in them. */
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainenstatic int maildir_save_finish_received_date(struct maildir_save_context *ctx,
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen struct mail_storage *storage = &ctx->mbox->storage->storage;
73b50eecfc31750a312e2f940023f522eb07178cTimo Sirainen /* set the received_date by modifying mtime */
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen /* hardlinked */
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;
for (i = 0; i < count; i++) {
int ret;
T_BEGIN {
} T_END;
first_recent_uid = 0;
if (first_recent_uid != 0) {
struct index_transaction_context *t =
int ret = 0;
ret = 0;
T_BEGIN {
const char *dest;
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 &&
if (ret == 0) {
ret == 0) < 0)
if (ret < 0)
if (ret < 0) {