maildir-save.c revision aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89
/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "buffer.h"
#include "istream.h"
#include "istream-crlf.h"
#include "ostream.h"
#include "fdatasync-path.h"
#include "str.h"
#include "index-mail.h"
#include "maildir-storage.h"
#include "maildir-uidlist.h"
#include "maildir-keywords.h"
#include "maildir-filename.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;
unsigned int files_count;
int fd;
unsigned int want_mails:1;
unsigned int have_keywords:1;
unsigned int locked:1;
unsigned int failed:1;
unsigned int moving:1;
unsigned int finished:1;
};
bool newdir)
{
int ret;
/* 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;
} else {
"rename(%s, %s) failed: %m",
}
}
return ret;
}
struct maildir_save_context *
{
struct maildir_save_context *ctx;
sizeof(unsigned int));
return ctx;
}
struct mail_keywords *keywords,
{
struct maildir_filename *mf;
/* 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 */
ctx->files_count++;
/* @UNSAFE */
}
/* insert into index */
}
}
struct mailbox_transaction_context *_t =
&t->ictx.mailbox_ctx;
}
}
/* FIXME: copying with hardlinking. we could copy the
cached data directly */
} 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 **fname_r)
{
unsigned int prefix_len;
int fd;
for (;;) {
/* stat() first to see if it exists. pretty much the only
possibility of that happening is if time had moved
backwards, but even then it's highly unlikely. */
/* try another file name */
return -1;
} else {
/* doesn't exist */
break;
/* race condition between stat() and open().
highly unlikely. */
}
}
if (fd == -1) {
} else {
}
}
}
return fd;
}
struct mail_save_context *
{
struct maildir_transaction_context *t =
(struct maildir_transaction_context *)_t;
t->save_ctx = maildir_save_transaction_init(t);
}
{
struct maildir_transaction_context *t =
T_BEGIN {
/* create a new file in tmp/ directory */
else {
else
}
} T_END;
}
}
{
return -1;
do {
if (!mail_storage_set_error_from_errno(storage)) {
"o_stream_send_istream(%s/%s) "
"failed: %m",
}
return -1;
}
/* 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. */
return 0;
}
{
const char *path;
int output_errno;
/* tmp file creation failed */
return -1;
}
if (!mail_storage_set_error_from_errno(storage)) {
"o_stream_flush(%s) failed: %m", path);
}
}
/* set the received_date by modifying mtime */
"utime(%s) failed: %m", path);
}
else {
"fstat(%s) failed: %m", path);
}
} else {
/* hardlinked */
else {
"stat(%s) failed: %m", path);
}
}
}
/* remember the size in case we want to add it to filename */
if (!mail_storage_set_error_from_errno(storage)) {
"fsync(%s) failed: %m", path);
}
}
}
if (!mail_storage_set_error_from_errno(storage)) {
"close(%s) failed: %m", path);
}
}
struct maildir_filename **fm;
/* delete the tmp file */
"unlink(%s) failed: %m", path);
}
} else if (errno != 0) {
}
/* remove from the linked list */
ctx->files_count--;
return -1;
}
return 0;
}
{
int ret;
T_BEGIN {
} T_END;
return ret;
}
{
(void)maildir_save_finish(_ctx);
}
static void
struct maildir_filename *pos)
{
struct maildir_filename *mf;
/* try to unlink the mails already moved */
T_BEGIN {
} T_END;
}
}
}
bool new_changed, bool cur_changed)
{
return 0;
if (new_changed) {
return -1;
}
}
if (cur_changed) {
return -1;
}
}
return 0;
}
static int
{
struct maildir_transaction_context *t =
int ret;
/* we'll need to keep the lock past the sync deinit */
return -1;
if (maildir_sync_header_refresh(mbox) < 0)
return -1;
return -1;
/* 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;
/* if messages were added to index, assign them UIDs */
/* these mails are all recent in our session */
/* maildir_sync_index() dropped recent flags from
existing messages. we'll still need to drop recent
flags from these newly added messages. */
offsetof(struct mail_index_header,
}
/* this will work even if index isn't updated */
return 0;
}
{
struct maildir_transaction_context *t =
struct maildir_filename *mf;
int ret;
/* if we want to assign UIDs or keywords, we require uidlist lock */
!ctx->have_keywords) {
/* assign the UIDs if we happen to get a lock */
}
&ctx->uidlist_sync_ctx);
if (ret <= 0 &&
return -1;
}
if (maildir_transaction_save_commit_pre_sync(ctx) < 0) {
return -1;
}
} else {
/* since we couldn't lock uidlist, we'll have to drop the
appends to index. */
t->ictx.cache_trans =
}
ret = 0;
T_BEGIN {
const char *dest;
/* 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. */
ret = 0;
else {
if (newdir)
new_changed = TRUE;
else
cur_changed = TRUE;
}
} T_END;
if (ret < 0)
break;
}
if (ret == 0) {
}
/* everything was moved successfully. update our internal
state. */
const char *dest;
if (newdir)
} T_END;
}
/* update dovecot-uidlist file. */
ret >= 0) < 0)
ret = -1;
}
*t->ictx.saved_uid_validity =
/* Mail freeing may trigger cache updates and a call to
maildir_save_file_get_path(). Do this before finishing index
sync so we still have keywords_sync_ctx. */
}
/* It doesn't matter if index syncing fails */
}
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 -1;
}
return ret;
}
{
}
static void
{
struct maildir_filename *mf;
/* clean up the temp files */
} else {
}
}
if (hardlinks)
}
{
T_BEGIN {
} T_END;
}