mail-transaction-log-file.c revision 6ade0d76250bc7db405cd20f3e1b660f74c77bd4
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose/* Copyright (c) 2003-2016 Dovecot authors, see the included COPYING file */
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose#define MEMORY_LOG_NAME "(in-memory transaction log file)"
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bosemail_transaction_log_file_sync(struct mail_transaction_log_file *file);
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Boselog_file_set_syscall_error(struct mail_transaction_log_file *file,
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose mail_index_file_set_syscall_error(file->log->index,
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bosemail_transaction_log_mark_corrupted(struct mail_transaction_log_file *file)
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose unsigned int offset =
8167761a1e1d7575d49babcea45937fc9cd45fdcSumit Bose offsetof(struct mail_transaction_log_header, indexid);
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose /* indexid=0 marks the log file as corrupted. we opened the file with
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose O_APPEND, and now we need to drop it for pwrite() to work (at least
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose in Linux) */
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose mail_index_file_set_syscall_error(file->log->index,
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose if (fcntl(file->fd, F_SETFL, flags & ~O_APPEND) < 0) {
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose mail_index_file_set_syscall_error(file->log->index,
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose mail_index_file_set_syscall_error(file->log->index,
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bosemail_transaction_log_file_set_corrupted(struct mail_transaction_log_file *file,
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose const char *fmt, ...)
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose "Corrupted transaction log file %s seq %u: %s "
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bosemail_transaction_log_file_alloc(struct mail_transaction_log *log,
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose const char *path)
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose file = i_new(struct mail_transaction_log_file, 1);
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bosevoid mail_transaction_log_file_free(struct mail_transaction_log_file **_file)
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose for (p = &file->log->files; *p != NULL; p = &(*p)->next) {
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose if (*p == file) {
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose if (munmap(file->mmap_base, file->mmap_size) < 0)
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bosemail_transaction_log_file_skip_to_head(struct mail_transaction_log_file *file)
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose const struct mail_index_modseq_header *modseq_hdr;
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose if (map == NULL || file->hdr.file_seq != map->hdr.log_file_seq ||
8167761a1e1d7575d49babcea45937fc9cd45fdcSumit Bose /* we can get a valid log offset from index file. initialize
8167761a1e1d7575d49babcea45937fc9cd45fdcSumit Bose sync_offset from it so we don't have to read the whole log
8167761a1e1d7575d49babcea45937fc9cd45fdcSumit Bose file from beginning. */
8167761a1e1d7575d49babcea45937fc9cd45fdcSumit Bose modseq_hdr = mail_index_map_get_modseq_header(map);
8167761a1e1d7575d49babcea45937fc9cd45fdcSumit Bose "%s: log_file_head_offset too small",
8167761a1e1d7575d49babcea45937fc9cd45fdcSumit Bose file->sync_highest_modseq = file->hdr.initial_modseq;
8167761a1e1d7575d49babcea45937fc9cd45fdcSumit Bose } else if (modseq_hdr == NULL && file->hdr.initial_modseq == 0) {
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose /* modseqs not used yet */
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose /* highest_modseq not synced, start from beginning */
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose file->sync_highest_modseq = file->hdr.initial_modseq;
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose } else if (modseq_hdr->log_offset > head_offset) {
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose "%s: modseq_hdr.log_offset too large",
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose file->sync_highest_modseq = file->hdr.initial_modseq;
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose /* start from where we last stopped tracking modseqs */
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose file->sync_highest_modseq = modseq_hdr->highest_modseq;
8167761a1e1d7575d49babcea45937fc9cd45fdcSumit Bose if (file->hdr.file_seq == log->index->map->hdr.log_file_seq) {
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose file->saved_tail_sync_offset = file->saved_tail_offset;
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose if (file->saved_tail_offset > file->max_tail_offset)
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bosemail_transaction_log_file_add_to_list(struct mail_transaction_log_file *file)
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose file->sync_highest_modseq = file->hdr.initial_modseq;
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose /* insert it to correct position */
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose for (p = &file->log->files; *p != NULL; p = &(*p)->next) {
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose i_assert((*p)->hdr.file_seq < file->hdr.file_seq);
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose /* if we read any unfinished data, make sure the buffer gets
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose truncated. */
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bosemail_transaction_log_init_hdr(struct mail_transaction_log *log,
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose hdr->major_version = MAIL_TRANSACTION_LOG_MAJOR_VERSION;
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose hdr->minor_version = MAIL_TRANSACTION_LOG_MINOR_VERSION;
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose hdr->hdr_size = sizeof(struct mail_transaction_log_header);
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose hdr->compat_flags |= MAIL_INDEX_COMPAT_LITTLE_ENDIAN;
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose /* not creating index - make sure we have latest header */
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose /* if we got here from mapping, the .log file is
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose corrupted. use whatever values we got from index
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose hdr->prev_file_seq = index->map->hdr.log_file_seq;
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose hdr->prev_file_offset = index->map->hdr.log_file_head_offset;
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose hdr->file_seq = index->map->hdr.log_file_seq + 1;
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose /* modseq tracking in log files is required for many reasons
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose nowadays, even if per-message modseqs aren't enabled in
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose dovecot.index. */
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose /* make sure the sequence always increases to avoid crashes
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose later. this catches the buggy case where two processes
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose happen to replace the same log file. */
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose for (file = log->head->next; file != NULL; file = file->next) {
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose /* make sure the sequence grows */
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose if (hdr->initial_modseq < log->head->sync_highest_modseq) {
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose /* this should be always up-to-date */
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose hdr->initial_modseq = log->head->sync_highest_modseq;
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bosemail_transaction_log_file_alloc_in_memory(struct mail_transaction_log *log)
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose file = mail_transaction_log_file_alloc(log, MEMORY_LOG_NAME);
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose if (mail_transaction_log_init_hdr(log, &file->hdr) < 0) {
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose file->buffer = buffer_create_dynamic(default_pool, 4096);
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bosemail_transaction_log_file_dotlock(struct mail_transaction_log_file *file)
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose mail_transaction_log_get_dotlock_set(file->log, &dotlock_set);
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose ret = file_dotlock_create(&dotlock_set, file->filepath, 0,
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose log_file_set_syscall_error(file, "file_dotlock_create()");
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose "Timeout (%us) while waiting for "
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose "dotlock for transaction log file %s",
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bosemail_transaction_log_file_undotlock(struct mail_transaction_log_file *file)
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose log_file_set_syscall_error(file, "file_dotlock_delete()");
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose "Dotlock was lost for transaction log file %s",
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Boseint mail_transaction_log_file_lock(struct mail_transaction_log_file *file)
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose if (file->log->index->lock_method == FILE_LOCK_METHOD_DOTLOCK)
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose "Index is read-only, can't write-lock %s",
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose lock_timeout_secs = I_MIN(MAIL_TRANSACTION_LOG_LOCK_TIMEOUT,
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose ret = mail_index_lock_fd(file->log->index, file->filepath, file->fd,
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose log_file_set_syscall_error(file, "mail_index_wait_lock_fd()");
const char *lock_reason)
unsigned int lock_time;
static ssize_t
void *dest;
yet (hdr.size=0), the buffer must be truncated later */
pos = 0;
if (ret > 0)
int ret;
ret = 0;
return ret;
bool ignore_estale)
struct mail_transaction_log_file *f;
int ret;
if (ret < 0) {
#if !WORDS_BIGENDIAN
return mail_transaction_log_file_fail_dupe(f);
bool ignore_estale)
return TRUE;
return FALSE;
unsigned int hdr_offset;
const char *path2;
if (reset)
FALSE) > 0 &&
if (reset) {
if (need_lock) {
if (ret < 0)
if (rename_existing) {
bool reset)
if (ret < 0) {
return ret;
bool ignore_estale;
int ret;
if (ret > 0) {
if (ret == 0) {
i == MAIL_INDEX_ESTALE_RETRY_COUNT) {
const unsigned int offset_pos =
sizeof(tail_offset));
if (*cur_modseq != 0) {
const unsigned int modseq_ext_len =
modseq_ext_len) == 0) {
case MAIL_TRANSACTION_APPEND:
case MAIL_TRANSACTION_MODSEQ_UPDATE: {
static struct modseq_cache *
if (idx > 0) {
static struct modseq_cache *
return NULL;
best = i;
return NULL;
static struct modseq_cache *
return NULL;
best = i;
return NULL;
int ret;
if (ret <= 0) {
if (ret < 0)
int ret;
if (ret <= 0) {
if (ret < 0)
unsigned int trans_size)
int ret;
if (ret != 0)
case MAIL_TRANSACTION_BOUNDARY: {
const void *data;
int ret;
if (trans_size == 0) {
if (ret < 0)
void *data;
if (ret > 0) {
if (ret == 0) {
void *data;
if (ret > 0)
if (ret < 0) {
return TRUE;
return TRUE;
return FALSE;
int ret;
if (ret <= 0)
return ret;
return ret;
MADV_SEQUENTIAL) < 0)
int ret;
if (ret > 0)
FALSE);
} while (ret == 0);
int ret;
end_offset) == 0)
if (ret <= 0)
return ret;
*file)