maildir-save.c revision e32cf1b4430c4a5f28c9b5bb8b1366e7b06ed68d
/* Copyright (C) 2002-2004 Timo Sirainen */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "buffer.h"
#include "istream.h"
#include "istream-tee.h"
#include "ostream.h"
#include "ostream-crlf.h"
#include "str.h"
#include "index-mail.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"
#include "maildir-sync.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <utime.h>
struct maildir_filename {
struct maildir_filename *next;
const char *basename;
enum mail_flags flags;
unsigned int keywords_count;
/* unsigned int keywords[]; */
};
struct maildir_save_context {
struct mail_save_context ctx;
struct maildir_mailbox *mbox;
struct mail_index_transaction *trans;
struct maildir_uidlist_sync_ctx *uidlist_sync_ctx;
struct maildir_index_sync_context *sync_ctx;
int fd;
unsigned int want_mails:1;
unsigned int failed:1;
unsigned int moving:1;
unsigned int finished:1;
};
bool newdir)
{
int ret;
t_push();
/* if we have flags, we'll move it to cur/ directly, because files in
new/ directory can't have flags. alternative would be to write it
in new/ and set the flags dirty in index file, but in that case
external MUAs would see wrong flags. */
/* maildir spec says we should use link() + unlink() here. however
since our filename is guaranteed to be unique, rename() works just
as well, except faster. even if the filename wasn't unique, the
problem could still happen if the file was already moved from
new/ to cur/, so link() doesn't really provide any safety anyway.
Besides the small temporary performance benefits, this rename() is
almost required with OSX's HFS+ filesystem, since it implements
hard links in a pretty ugly way, which makes the performance crawl
when a lot of hard links are used. */
ret = 0;
else {
ret = -1;
"Not enough disk space");
} else {
"rename(%s, %s) failed: %m",
}
}
t_pop();
return ret;
}
struct maildir_save_context *
{
struct maildir_save_context *ctx;
/* we'll do a quick check here to see if maildir is currently in
synced state. in that case it's cheap to update index file.
this can't be completely trusted because uidlist isn't locked,
but if there are some changes we can deal with it. */
sizeof(unsigned int));
return ctx;
}
{
struct maildir_filename *mf;
struct mail_keywords *kw;
if (mf->keywords_count != 0) {
t_push();
/* @UNSAFE */
mf->keywords_count);
mf->keywords_count);
MODIFY_REPLACE, kw);
t_pop();
}
}
}
struct mail_keywords *keywords,
{
struct maildir_filename *mf;
struct tee_istream *tee;
/* if there are any existing mails, we need to append them
to index here to keep the UIDs correct */
}
/* now, we want to be able to rollback the whole append session,
so we'll just store the name of this temp file and move it later
into new/ or cur/. */
/* @UNSAFE */
/* @UNSAFE */
}
if (ctx->want_mails) {
/* insert into index */
}
}
struct mailbox_transaction_context *_t =
&t->ictx.mailbox_ctx;
}
}
i_unreached();
} else {
}
}
static bool
struct maildir_filename *mf,
const char **fname_r)
{
}
if (mf->keywords_count == 0) {
return TRUE;
}
return FALSE;
}
mf->keywords_count * sizeof(unsigned int));
&ctx->keywords_array);
return FALSE;
}
struct maildir_filename *mf)
{
const char *fname;
/* file is still in tmp/ */
}
/* already moved to new/ or cur/ */
else
}
{
struct maildir_transaction_context *t =
(struct maildir_transaction_context *)_t;
struct maildir_filename *mf;
while (seq > 0) {
seq--;
}
}
const char *from_envelope __attr_unused__,
struct mail_save_context **ctx_r)
{
struct maildir_transaction_context *t =
(struct maildir_transaction_context *)_t;
struct maildir_save_context *ctx;
t_push();
t->save_ctx = maildir_save_transaction_init(t);
/* create a new file in tmp/ directory */
&path);
t_pop();
return -1;
}
fname++;
MAIL_STORAGE_FLAG_SAVE_CRLF) != 0 ?
flags &= ~MAIL_RECENT;
flags |= MAIL_RECENT;
t_pop();
}
{
return -1;
"Not enough disk space");
} else {
"o_stream_send_istream(%s/%s) failed: %m",
}
return -1;
}
return 0;
}
{
const char *path;
int output_errno;
}
/* tmp file creation failed */
return -1;
}
/* remember the size in case we want to add it to filename */
t_push();
/* set the received_date by modifying mtime */
"utime(%s) failed: %m", path);
}
}
"fsync(%s) failed: %m", path);
}
}
"close(%s) failed: %m", path);
}
struct maildir_filename **fm;
/* delete the tmp file */
"unlink(%s) failed: %m", path);
}
"Not enough disk space");
} else if (errno != 0) {
}
/* remove from the linked list */
t_pop();
return -1;
}
t_pop();
return 0;
}
{
(void)maildir_save_finish(_ctx);
}
static void
struct maildir_filename *pos)
{
struct maildir_filename *mf;
/* try to unlink the mails already moved */
continue;
t_push();
t_pop();
}
}
{
struct maildir_filename *mf;
const char *dest;
int ret;
&ctx->uidlist_sync_ctx) <= 0) {
/* error or timeout - our transaction is broken */
return -1;
}
/* Start syncing so that keywords_sync_ctx gets set.. */
return -1;
}
if (ctx->want_mails) {
/* now that uidlist is locked, make sure all the existing mails
have been added to index. we don't really look into the
maildir, just add all the new mails listed in
dovecot-uidlist to index. */
return -1;
}
sync_commit = TRUE;
}
ret = 0;
t_push();
/* if hardlink-flag is set, the file is already in destination.
if the hardlinked mail contained keywords, it was linked
into tmp/ and it doesn't have the hardlink-flag set, so it's
treated as any other saved mail. */
}
if (ret == 0) {
}
t_pop();
}
/* if we didn't call maildir_sync_index() we could skip over
transactions by committing the changes */
!sync_commit) < 0)
ret = -1;
if (ret < 0) {
/* unlink the files we just moved in an attempt to rollback
the transaction. uidlist is still locked, so at least other
Dovecot instances haven't yet seen the files. */
/* returning failure finishes the save_context */
}
return ret;
}
{
/* uidlist locks the syncing. don't release it until save's transaction
has been written to disk. */
}
{
struct maildir_filename *mf;
t_push();
/* clean up the temp files */
} else {
}
}
if (hardlinks)
t_pop();
}