mail-transaction-log-file.c revision 1d738cce754bc64bbc66d3355ebdaf3f6eac55f1
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2003-2010 Dovecot authors, see the included COPYING file */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "lib.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "ioloop.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "buffer.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "file-dotlock.h"
08d6658a4e2ec8104cd1307f6baa75fdb07a24f8Mark Washenberger#include "nfs-workarounds.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "read-full.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "write-full.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "mmap-util.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "mail-index-private.h"
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen#include "mail-index-modseq.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#include "mail-transaction-log-private.h"
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#define LOG_PREFETCH IO_BLOCK_SIZE
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen#define MEMORY_LOG_NAME "(in-memory transaction log file)"
25d624dd86700c82cd28427f3d3bebe7c8f7f459Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenlog_file_set_syscall_error(struct mail_transaction_log_file *file,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *function)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return mail_index_file_set_syscall_error(file->log->index,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->filepath, function);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenmail_transaction_log_mark_corrupted(struct mail_transaction_log_file *file)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen unsigned int offset =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen offsetof(struct mail_transaction_log_header, indexid);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file) ||
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->log->index->readonly)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* indexid=0 marks the log file as corrupted */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (pwrite_full(file->fd, &file->hdr.indexid,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sizeof(file->hdr.indexid), offset) < 0) {
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->filepath, "pwrite()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenvoid
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenmail_transaction_log_file_set_corrupted(struct mail_transaction_log_file *file,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *fmt, ...)
bd63b5b860658b01b1f46f26d406e1e4a9dc019aTimo Sirainen{
15a07b47846c47a81d69a14d649564e222d6f742Timo Sirainen va_list va;
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen
c1faff067b29fb48426cb84260adba563e93189aTimo Sirainen file->corrupted = TRUE;
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen file->hdr.indexid = 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_transaction_log_mark_corrupted(file);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen va_start(va, fmt);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen T_BEGIN {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_index_set_error(file->log->index,
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen "Corrupted transaction log file %s seq %u: %s "
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "(sync_offset=%"PRIuUOFF_T")",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->filepath, file->hdr.file_seq,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen t_strdup_vprintf(fmt, va), file->sync_offset);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen } T_END;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen va_end(va);
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen}
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainenstruct mail_transaction_log_file *
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenmail_transaction_log_file_alloc(struct mail_transaction_log *log,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *path)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mail_transaction_log_file *file;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen file = i_new(struct mail_transaction_log_file, 1);
1d2b188f0eedc3cab6e27ceac5425a037f38042eTimo Sirainen file->log = log;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen file->filepath = i_strdup(path);
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen file->fd = -1;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen return file;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen}
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainenvoid mail_transaction_log_file_free(struct mail_transaction_log_file **_file)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen struct mail_transaction_log_file *file = *_file;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen struct mail_transaction_log_file **p;
eacce2276278ce6a8176a9a100807dba50bbfb36Timo Sirainen int old_errno = errno;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen *_file = NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_transaction_log_file_unlock(file);
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen for (p = &file->log->files; *p != NULL; p = &(*p)->next) {
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen if (*p == file) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen *p = file->next;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen break;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file == file->log->head)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->log->head = NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->buffer != NULL)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen buffer_free(&file->buffer);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->mmap_base != NULL) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (munmap(file->mmap_base, file->mmap_size) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen log_file_set_syscall_error(file, "munmap()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->fd != -1) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (close(file->fd) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen log_file_set_syscall_error(file, "close()");
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_free(file->filepath);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_free(file);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen errno = old_errno;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenmail_transaction_log_file_skip_to_head(struct mail_transaction_log_file *file)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mail_transaction_log *log = file->log;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mail_index_map *map = log->index->map;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const struct mail_index_modseq_header *modseq_hdr;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen uoff_t head_offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (map == NULL || file->hdr.file_seq != map->hdr.log_file_seq ||
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen map->hdr.log_file_head_offset == 0)
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen return;
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* we can get a valid log offset from index file. initialize
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sync_offset from it so we don't have to read the whole log
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file from beginning. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen head_offset = map->hdr.log_file_head_offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen modseq_hdr = mail_index_map_get_modseq_header(map);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (head_offset < file->hdr.hdr_size) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_index_set_error(log->index,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "%s: log_file_head_offset too small",
3a9eb305fd4aad5502cb7e64625874385ab5bc19Timo Sirainen log->index->filepath);
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen file->sync_offset = file->hdr.hdr_size;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen file->sync_highest_modseq = file->hdr.initial_modseq;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (modseq_hdr == NULL && file->hdr.initial_modseq == 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* modseqs not used yet */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->sync_offset = head_offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->sync_highest_modseq = 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (modseq_hdr == NULL ||
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen modseq_hdr->log_seq != file->hdr.file_seq) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* highest_modseq not synced, start from beginning */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->sync_offset = file->hdr.hdr_size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->sync_highest_modseq = file->hdr.initial_modseq;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (modseq_hdr->log_offset > head_offset) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_index_set_error(log->index,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "%s: modseq_hdr.log_offset too large",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen log->index->filepath);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->sync_offset = file->hdr.hdr_size;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->sync_highest_modseq = file->hdr.initial_modseq;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* start from where we last stopped tracking modseqs */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->sync_offset = modseq_hdr->log_offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->sync_highest_modseq = modseq_hdr->highest_modseq;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->hdr.file_seq == log->index->map->hdr.log_file_seq) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->saved_tail_offset =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen log->index->map->hdr.log_file_tail_offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->saved_tail_sync_offset = file->saved_tail_offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->saved_tail_offset > file->max_tail_offset)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->max_tail_offset = file->saved_tail_offset;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic void
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainenmail_transaction_log_file_add_to_list(struct mail_transaction_log_file *file)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen{
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen struct mail_transaction_log_file **p;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen file->sync_offset = file->hdr.hdr_size;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen file->sync_highest_modseq = file->hdr.initial_modseq;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen mail_transaction_log_file_skip_to_head(file);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen /* insert it to correct position */
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen for (p = &file->log->files; *p != NULL; p = &(*p)->next) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if ((*p)->hdr.file_seq > file->hdr.file_seq)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen break;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen i_assert((*p)->hdr.file_seq < file->hdr.file_seq);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen file->next = *p;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen *p = file;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen}
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainenstatic int
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainenmail_transaction_log_init_hdr(struct mail_transaction_log *log,
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen struct mail_transaction_log_header *hdr)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen{
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen struct mail_index *index = log->index;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen memset(hdr, 0, sizeof(*hdr));
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen hdr->major_version = MAIL_TRANSACTION_LOG_MAJOR_VERSION;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen hdr->minor_version = MAIL_TRANSACTION_LOG_MINOR_VERSION;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen hdr->hdr_size = sizeof(struct mail_transaction_log_header);
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen hdr->indexid = log->index->indexid;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen hdr->create_stamp = ioloop_time;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen#if !WORDS_BIGENDIAN
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen hdr->compat_flags |= MAIL_INDEX_COMPAT_LITTLE_ENDIAN;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen#endif
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (index->fd != -1) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen /* not creating index - make sure we have latest header */
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen if (!index->mapping) {
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen if (mail_index_map(index,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen MAIL_INDEX_SYNC_HANDLER_HEAD) <= 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* if we got here from mapping, the .log file is
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen corrupted. use whatever values we got from index
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen file */
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (index->map != NULL) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen hdr->prev_file_seq = index->map->hdr.log_file_seq;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen hdr->prev_file_offset = index->map->hdr.log_file_head_offset;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen hdr->file_seq = index->map->hdr.log_file_seq + 1;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen hdr->initial_modseq =
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_index_map_modseq_get_highest(index->map);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen hdr->file_seq = 1;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (log->head != NULL) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (hdr->file_seq <= log->head->hdr.file_seq) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen /* make sure the sequence grows */
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen hdr->file_seq = log->head->hdr.file_seq+1;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (hdr->initial_modseq < log->head->sync_highest_modseq) {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen /* this should be always up-to-date */
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen hdr->initial_modseq = log->head->sync_highest_modseq;
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen }
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen }
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen return 0;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen}
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainenstruct mail_transaction_log_file *
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainenmail_transaction_log_file_alloc_in_memory(struct mail_transaction_log *log)
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen{
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen struct mail_transaction_log_file *file;
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file = mail_transaction_log_file_alloc(log, MEMORY_LOG_NAME);
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen if (mail_transaction_log_init_hdr(log, &file->hdr) < 0) {
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen i_free(file);
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen return NULL;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
34015eb0b74735f2fac07c12697bde20a94735e6Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->buffer = buffer_create_dynamic(default_pool, 4096);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->buffer_offset = sizeof(file->hdr);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen mail_transaction_log_file_add_to_list(file);
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen return file;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen}
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainenstatic int
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainenmail_transaction_log_file_dotlock(struct mail_transaction_log_file *file)
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen{
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen int ret;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen if (file->log->dotlock_count > 0)
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen ret = 1;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen else {
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen ret = file_dotlock_create(&file->log->dotlock_settings,
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen file->filepath, 0,
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen &file->log->dotlock);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret > 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->log->dotlock_count++;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->locked = TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen }
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen if (ret < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen log_file_set_syscall_error(file, "file_dotlock_create()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_index_set_error(file->log->index,
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen "Timeout while waiting for "
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen "dotlock for transaction log file %s",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->filepath);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->log->index->index_lock_timeout = TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenmail_transaction_log_file_undotlock(struct mail_transaction_log_file *file)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int ret;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (--file->log->dotlock_count > 0)
0b2f7be9fadfd4026a9174e51170890cde3edf48Timo Sirainen return 0;
45c872f65e4f327ef166c6e2b71bb43e188ac562Timo Sirainen
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen ret = file_dotlock_delete(&file->log->dotlock);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen log_file_set_syscall_error(file, "file_dotlock_delete()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret == 0) {
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen mail_index_set_error(file->log->index,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "Dotlock was lost for transaction log file %s",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->filepath);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
ec5fec7eab19e134a2607b7e224b3e14a1771ee0Timo Sirainenint mail_transaction_log_file_lock(struct mail_transaction_log_file *file)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen if (file->locked)
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen return 0;
ec5fec7eab19e134a2607b7e224b3e14a1771ee0Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen file->locked = TRUE;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return 0;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (file->log->index->lock_method == FILE_LOCK_METHOD_DOTLOCK)
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen return mail_transaction_log_file_dotlock(file);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen i_assert(file->file_lock == NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = mail_index_lock_fd(file->log->index, file->filepath, file->fd,
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen F_WRLCK, MAIL_TRANSCATION_LOG_LOCK_TIMEOUT,
4145cbac82bfc0c8bfeceeca0ef841700117930cTimo Sirainen &file->file_lock);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret > 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->locked = TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen log_file_set_syscall_error(file, "mail_index_wait_lock_fd()");
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen return -1;
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen }
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen
04870054863757edf048c81dcce3c5e7dec453cdTimo Sirainen mail_index_set_error(file->log->index,
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen "Timeout while waiting for lock for transaction log file %s",
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen file->filepath);
e593e507ee5ea3869271a631874c5c4b5c7a294dTimo Sirainen file->log->index->index_lock_timeout = TRUE;
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen return -1;
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen}
e1203014de25c8c3d3975a9f4b4a04616df4bba2Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenvoid mail_transaction_log_file_unlock(struct mail_transaction_log_file *file)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (!file->locked)
ec5fec7eab19e134a2607b7e224b3e14a1771ee0Timo Sirainen return;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->locked = FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file))
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return;
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->log->index->lock_method == FILE_LOCK_METHOD_DOTLOCK) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_transaction_log_file_undotlock(file);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file_unlock(&file->file_lock);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic ssize_t
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainenmail_transaction_log_file_read_header(struct mail_transaction_log_file *file)
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ssize_t pos, ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen memset(&file->hdr, 0, sizeof(file->hdr));
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* try to read the whole header, but it's not necessarily an error to
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen read less since the older versions of the log format could be
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen smaller. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen pos = 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen do {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = pread(file->fd, PTR_OFFSET(&file->hdr, pos),
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen sizeof(file->hdr) - pos, pos);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret > 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen pos += ret;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } while (ret > 0 && pos < (ssize_t)sizeof(file->hdr));
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return ret < 0 ? -1 : pos;
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenstatic int
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenmail_transaction_log_file_read_hdr(struct mail_transaction_log_file *file,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen bool ignore_estale)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mail_transaction_log_file *f;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int ret;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen i_assert(!MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file));
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen if (file->corrupted)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = mail_transaction_log_file_read_header(file);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (errno != ESTALE || !ignore_estale)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen log_file_set_syscall_error(file, "pread()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->hdr.major_version != MAIL_TRANSACTION_LOG_MAJOR_VERSION) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* incompatible version - fix silently */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret < MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE) {
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen mail_transaction_log_file_set_corrupted(file,
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen "unexpected end of file while reading header");
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen return 0;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen }
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen if (file->hdr.minor_version >= 2 || file->hdr.major_version > 1) {
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen /* we have compatibility flags */
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen enum mail_index_header_compat_flags compat_flags = 0;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen#if !WORDS_BIGENDIAN
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen compat_flags |= MAIL_INDEX_COMPAT_LITTLE_ENDIAN;
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen#endif
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen if (file->hdr.compat_flags != compat_flags) {
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen /* architecture change */
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen mail_index_set_error(file->log->index,
e5b723864630e40c9028808ef417dd3d6fbf495bTimo Sirainen "Rebuilding index file %s: "
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen "CPU architecture changed",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->log->index->filepath);
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->hdr.hdr_size < MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_transaction_log_file_set_corrupted(file,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen "Header size too small");
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen return 0;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (file->hdr.hdr_size < sizeof(file->hdr)) {
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* @UNSAFE: smaller than we expected - zero out the fields we
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen shouldn't have filled */
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen memset(PTR_OFFSET(&file->hdr, file->hdr.hdr_size), 0,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen sizeof(file->hdr) - file->hdr.hdr_size);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen if (file->hdr.indexid == 0) {
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen /* corrupted */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->corrupted = TRUE;
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen mail_index_set_error(file->log->index,
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen "Transaction log file %s: marked corrupted",
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen file->filepath);
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen return 0;
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen }
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen if (file->hdr.indexid != file->log->index->indexid) {
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen if (file->log->index->indexid != 0 &&
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen !file->log->index->initial_create) {
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen /* index file was probably just rebuilt and we don't
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen know about it yet */
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen mail_transaction_log_file_set_corrupted(file,
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen "indexid changed %u -> %u",
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen file->log->index->indexid, file->hdr.indexid);
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen /* creating index file. since transaction log is created
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen first, use the indexid in it to create the main index
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen to avoid races. */
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen file->log->index->indexid = file->hdr.indexid;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* make sure we already don't have a file with the same sequence
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen opened. it shouldn't happen unless the old log file was
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen corrupted. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen for (f = file->log->files; f != NULL; f = f->next) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (f->hdr.file_seq == file->hdr.file_seq) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* mark the old file corrupted. we can't safely remove
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen it from the list however, so return failure. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen f->corrupted = TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen f->hdr.indexid = 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (strcmp(f->filepath, f->log->head->filepath) != 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* only mark .2 corrupted, just to make sure
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen we don't lose any changes from .log in case
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen we're somehow wrong */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_transaction_log_mark_corrupted(f);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = -1;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen mail_index_set_error(f->log->index,
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen "Transaction log %s: "
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen "duplicate transaction log sequence (%u)",
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen f->filepath, f->hdr.file_seq);
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return ret;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen file->sync_highest_modseq = file->hdr.initial_modseq;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return 1;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen}
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainenstatic int
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainenmail_transaction_log_file_stat(struct mail_transaction_log_file *file,
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen bool ignore_estale)
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen{
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen struct stat st;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (fstat(file->fd, &st) < 0) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen if (errno != ESTALE || !ignore_estale)
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen log_file_set_syscall_error(file, "fstat()");
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return -1;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen }
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen file->st_dev = st.st_dev;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen file->st_ino = st.st_ino;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen file->last_mtime = st.st_mtime;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen file->last_size = st.st_size;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen return 0;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen}
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainenstatic bool
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainenmail_transaction_log_file_is_dupe(struct mail_transaction_log_file *file)
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen{
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen struct mail_transaction_log_file *tmp;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen for (tmp = file->log->files; tmp != NULL; tmp = tmp->next) {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (tmp->st_ino == file->st_ino &&
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen CMP_DEV_T(tmp->st_dev, file->st_dev))
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return TRUE;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return FALSE;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen}
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainenstatic int
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainenmail_transaction_log_file_create2(struct mail_transaction_log_file *file,
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen int new_fd, bool reset,
31a574fda352ef4f71dbff9c30e15e4744e132c0Timo Sirainen struct dotlock **dotlock)
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen{
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen struct mail_index *index = file->log->index;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen struct stat st;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen const char *path2;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int fd, ret;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen bool rename_existing;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if ((file->log->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen /* although we check also mtime and file size below, it's done
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen only to fix broken log files. we don't bother flushing
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen attribute cache just for that. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen nfs_flush_file_handle_cache(file->filepath);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen /* log creation is locked now - see if someone already created it.
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen note that if we're rotating, we need to keep the log locked until
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen the file has been rewritten. and because fcntl() locks are stupid,
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen if we go and open()+close() the file and we had it already opened,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen its locks are lost. so we use stat() to check if the file has been
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen recreated, although it almost never is. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (reset)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen rename_existing = FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen else if (nfs_safe_stat(file->filepath, &st) < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (errno != ENOENT)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return log_file_set_syscall_error(file, "stat()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen rename_existing = FALSE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else if (st.st_ino == file->st_ino &&
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen CMP_DEV_T(st.st_dev, file->st_dev) &&
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* inode/dev checks are enough when we're rotating the file,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen but not when we're replacing a broken log file */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen st.st_mtime == file->last_mtime &&
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen (uoff_t)st.st_size == file->last_size) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* no-one else recreated the file */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen rename_existing = TRUE;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen } else {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* recreated. use the file if its header is ok */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen fd = nfs_safe_open(file->filepath, O_RDWR);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (fd == -1) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (errno != ENOENT) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen log_file_set_syscall_error(file, "open()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen } else {
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen file->fd = fd;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen if (mail_transaction_log_file_read_hdr(file,
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen FALSE) > 0 &&
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen mail_transaction_log_file_stat(file, FALSE) == 0) {
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen /* yes, it was ok */
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen (void)file_dotlock_delete(dotlock);
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen mail_transaction_log_file_add_to_list(file);
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen return 0;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen }
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen file->fd = -1;
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen if (close(fd) < 0)
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen log_file_set_syscall_error(file, "close()");
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen }
c3d40f3092af25cad9e807a85eaad4d92aab107bTimo Sirainen rename_existing = FALSE;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen if (mail_transaction_log_init_hdr(file->log, &file->hdr) < 0)
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen return -1;
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen if (reset) {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen /* don't reset modseqs. if we're reseting due to rebuilding
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen indexes we'll probably want to keep uidvalidity and in such
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen cases we really don't want to shrink modseqs. */
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen file->hdr.prev_file_seq = 0;
411f318ed3a25fa66c1b932e10df43841e2725c9Timo Sirainen file->hdr.prev_file_offset = 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (write_full(new_fd, &file->hdr, sizeof(file->hdr)) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return log_file_set_syscall_error(file, "write_full()");
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen
3e0bae44b65f5c46989fcef3d1e07203f496327eTimo Sirainen if ((file->log->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0) {
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen /* the header isn't important, so don't bother calling
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen fdatasync() unless NFS is used */
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen if (fdatasync(new_fd) < 0)
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen return log_file_set_syscall_error(file, "fdatasync()");
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen }
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen
bd4de9c8152e6ea032c1cb1df8b79635ff5ddf9eTimo Sirainen file->fd = new_fd;
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen ret = mail_transaction_log_file_stat(file, FALSE);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file->log->head != NULL && file->log->head->locked) {
4530cfa7456c10cd03fe9120c75f8bcb2f623ba4Timo Sirainen /* we'll need to preserve the lock */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (mail_transaction_log_file_lock(file) < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen ret = -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* if we return -1 the dotlock deletion code closes the fd */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->fd = -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (ret < 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* keep two log files */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (rename_existing) {
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen /* rename() would be nice and easy way to do this, except then
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen there's a race condition between the rename and
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen file_dotlock_replace(). during that time the log file
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen doesn't exist, which could cause problems. */
04a7b696e5255aa956277a0f7cabee736c69ec96Timo Sirainen path2 = t_strconcat(file->filepath, ".2", NULL);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (unlink(path2) < 0 && errno != ENOENT) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_index_set_error(index, "unlink(%s) failed: %m",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen path2);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* try to link() anyway */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (nfs_safe_link(file->filepath, path2, FALSE) < 0 &&
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen errno != ENOENT && errno != EEXIST) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_index_set_error(index, "link(%s, %s) failed: %m",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->filepath, path2);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* ignore the error. we don't care that much about the
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen second log file and we're going to overwrite this
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen first one. */
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen }
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen }
714e2da5096fb52b8845d3c79f9bb26225a606c9Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (file_dotlock_replace(dotlock,
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) <= 0)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* success */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->fd = new_fd;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_transaction_log_file_add_to_list(file);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainenint mail_transaction_log_file_create(struct mail_transaction_log_file *file,
e10d8b1291090c26b9ef499637e6e632485ca5beTimo Sirainen bool reset)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen{
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct mail_index *index = file->log->index;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen struct dotlock *dotlock;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen mode_t old_mask;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen int fd;
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen i_assert(!MAIL_INDEX_IS_IN_MEMORY(index));
6882df5fbca4a09cdaa95f54d70bb31b5920528cTimo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen if (file->log->index->readonly) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen mail_index_set_error(index,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen "Can't create log file %s: Index is read-only",
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen file->filepath);
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen return -1;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen }
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* With dotlocking we might already have path.lock created, so this
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen filename has to be different. */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen old_mask = umask(index->mode ^ 0666);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen fd = file_dotlock_open(&file->log->new_dotlock_settings,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen file->filepath, 0, &dotlock);
cd2cd224d3216a243d55c71c298a5b7684de0ac4Timo Sirainen umask(old_mask);
ac0fed903142d28ae3a1d5d00d2097fdf161b138Timo Sirainen
ac0fed903142d28ae3a1d5d00d2097fdf161b138Timo Sirainen if (fd == -1)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return log_file_set_syscall_error(file, "file_dotlock_open()");
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen mail_index_fchown(index, fd, file_dotlock_get_lock_path(dotlock));
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen /* either fd gets used or the dotlock gets deleted and returned fd
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen is for the existing file */
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (mail_transaction_log_file_create2(file, fd, reset, &dotlock) < 0) {
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen if (dotlock != NULL)
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen (void)file_dotlock_delete(&dotlock);
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen return -1;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
b66d803de86bfb411165b3465b0d9ef64ecfe2a1Timo Sirainen return 0;
02a54da28f376dd66d7939d8546a196a0045b486Timo Sirainen}
23fdad6c7e2581921f511e24cd9371c9eaebcef9Timo Sirainen
23fdad6c7e2581921f511e24cd9371c9eaebcef9Timo Sirainenint mail_transaction_log_file_open(struct mail_transaction_log_file *file,
8552b0cad8ffe9ccb8270577ba28b8010c89af11Timo Sirainen bool check_existing)
{
unsigned int i;
bool ignore_estale;
int ret;
for (i = 0;; i++) {
file->fd = nfs_safe_open(file->filepath, O_RDWR);
if (file->fd == -1) {
if (errno == ENOENT)
return 0;
return log_file_set_syscall_error(file, "open()");
}
ignore_estale = i < MAIL_INDEX_ESTALE_RETRY_COUNT;
if (mail_transaction_log_file_stat(file, ignore_estale) < 0)
ret = -1;
else if (check_existing &&
mail_transaction_log_file_is_dupe(file))
return 0;
else {
ret = mail_transaction_log_file_read_hdr(file,
ignore_estale);
}
if (ret > 0) {
/* success */
break;
}
if (ret == 0) {
/* corrupted */
if (file->log->index->readonly) {
/* don't delete */
} else if (unlink(file->filepath) < 0 &&
errno != ENOENT) {
mail_index_set_error(file->log->index,
"unlink(%s) failed: %m",
file->filepath);
}
return 0;
}
if (errno != ESTALE ||
i == MAIL_INDEX_ESTALE_RETRY_COUNT) {
/* syscall error */
return -1;
}
/* ESTALE - try again */
}
mail_transaction_log_file_add_to_list(file);
return 1;
}
static int
log_file_track_mailbox_sync_offset_hdr(struct mail_transaction_log_file *file,
const void *data, unsigned int size)
{
const struct mail_transaction_header_update *u = data;
const struct mail_index_header *ihdr;
const unsigned int offset_pos =
offsetof(struct mail_index_header, log_file_tail_offset);
const unsigned int offset_size = sizeof(ihdr->log_file_tail_offset);
uint32_t tail_offset;
i_assert(offset_size == sizeof(tail_offset));
if (size < sizeof(*u) || size < sizeof(*u) + u->size) {
mail_transaction_log_file_set_corrupted(file,
"header update extends beyond record size");
return -1;
}
if (u->offset <= offset_pos &&
u->offset + u->size >= offset_pos + offset_size) {
memcpy(&tail_offset,
CONST_PTR_OFFSET(u + 1, offset_pos - u->offset),
sizeof(tail_offset));
if (tail_offset < file->saved_tail_offset) {
if (file->sync_offset < file->saved_tail_sync_offset) {
/* saved_tail_offset was already set in header,
but we still had to resync the file to find
modseqs. ignore this record. */
return 1;
}
mail_index_set_error(file->log->index,
"Transaction log file %s seq %u: "
"log_file_tail_offset update shrank it "
"(%u vs %"PRIuUOFF_T" "
"sync_offset=%"PRIuUOFF_T")",
file->filepath, file->hdr.file_seq,
tail_offset, file->saved_tail_offset,
file->sync_offset);
} else {
file->saved_tail_offset = tail_offset;
if (tail_offset > file->max_tail_offset)
file->max_tail_offset = tail_offset;
return 1;
}
}
return 0;
}
void mail_transaction_update_modseq(const struct mail_transaction_header *hdr,
const void *data, uint64_t *cur_modseq)
{
uint32_t trans_size;
trans_size = mail_index_offset_to_uint32(hdr->size);
i_assert(trans_size != 0);
if (*cur_modseq != 0) {
/* tracking modseqs */
} else if ((hdr->type & MAIL_TRANSACTION_TYPE_MASK) ==
MAIL_TRANSACTION_EXT_INTRO) {
/* modseqs not tracked yet. see if this is a modseq
extension introduction. */
const struct mail_transaction_ext_intro *intro = data;
const unsigned int modseq_ext_len =
strlen(MAIL_INDEX_MODSEQ_EXT_NAME);
if (intro->name_size == modseq_ext_len &&
memcmp(intro + 1, MAIL_INDEX_MODSEQ_EXT_NAME,
modseq_ext_len) == 0) {
/* modseq tracking started */
*cur_modseq += 1;
return;
}
} else {
/* not tracking modseqs */
return;
}
switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
case MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT:
case MAIL_TRANSACTION_EXPUNGE_GUID | MAIL_TRANSACTION_EXPUNGE_PROT:
if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) {
/* ignore expunge requests */
break;
}
case MAIL_TRANSACTION_APPEND:
case MAIL_TRANSACTION_FLAG_UPDATE:
case MAIL_TRANSACTION_KEYWORD_UPDATE:
case MAIL_TRANSACTION_KEYWORD_RESET:
case MAIL_TRANSACTION_UID_UPDATE:
/* these changes increase modseq */
*cur_modseq += 1;
break;
case MAIL_TRANSACTION_MODSEQ_UPDATE: {
const struct mail_transaction_modseq_update *rec, *end;
end = CONST_PTR_OFFSET(data, trans_size - sizeof(*hdr));
for (rec = data; rec < end; rec++) {
uint64_t modseq = ((uint64_t)rec->modseq_high32 >> 32) |
rec->modseq_low32;
if (*cur_modseq < modseq)
*cur_modseq = modseq;
}
}
}
}
static struct modseq_cache *
modseq_cache_hit(struct mail_transaction_log_file *file, unsigned int idx)
{
struct modseq_cache cache;
if (idx > 0) {
/* @UNSAFE: move it to top */
cache = file->modseq_cache[idx];
memmove(file->modseq_cache + 1, file->modseq_cache,
sizeof(*file->modseq_cache) * idx);
file->modseq_cache[0] = cache;
}
return &file->modseq_cache[0];
}
static struct modseq_cache *
modseq_cache_get_offset(struct mail_transaction_log_file *file, uoff_t offset)
{
unsigned int i, best = -1U;
for (i = 0; i < N_ELEMENTS(file->modseq_cache); i++) {
if (offset < file->modseq_cache[i].offset)
continue;
if (file->modseq_cache[i].offset == 0)
return NULL;
if (offset == file->modseq_cache[i].offset) {
/* exact cache hit */
return modseq_cache_hit(file, i);
}
if (best == -1U ||
file->modseq_cache[i].offset <
file->modseq_cache[best].offset)
best = i;
}
if (best == -1U)
return NULL;
return &file->modseq_cache[best];
}
static struct modseq_cache *
modseq_cache_get_modseq(struct mail_transaction_log_file *file, uint64_t modseq)
{
unsigned int i, best = -1U;
for (i = 0; i < N_ELEMENTS(file->modseq_cache); i++) {
if (modseq < file->modseq_cache[i].highest_modseq)
continue;
if (file->modseq_cache[i].offset == 0)
return NULL;
if (modseq == file->modseq_cache[i].highest_modseq) {
/* exact cache hit */
return modseq_cache_hit(file, i);
}
if (best == -1U ||
file->modseq_cache[i].highest_modseq <
file->modseq_cache[best].highest_modseq)
best = i;
}
if (best == -1U)
return NULL;
return &file->modseq_cache[best];
}
static int
log_get_synced_record(struct mail_transaction_log_file *file, uoff_t *offset,
const struct mail_transaction_header **hdr_r)
{
const struct mail_transaction_header *hdr;
uint32_t trans_size;
hdr = CONST_PTR_OFFSET(file->buffer->data,
*offset - file->buffer_offset);
/* we've already synced this record at some point. it should
be valid. */
trans_size = mail_index_offset_to_uint32(hdr->size);
if (trans_size < sizeof(*hdr) ||
*offset - file->buffer_offset + trans_size > file->buffer->used) {
mail_transaction_log_file_set_corrupted(file,
"Transaction log corrupted unexpectedly at "
"%"PRIuUOFF_T": Invalid size %u (type=%x)",
*offset, trans_size, hdr->type);
return -1;
}
*offset += trans_size;
*hdr_r = hdr;
return 0;
}
int mail_transaction_log_file_get_highest_modseq_at(
struct mail_transaction_log_file *file,
uoff_t offset, uint64_t *highest_modseq_r)
{
const struct mail_transaction_header *hdr;
struct modseq_cache *cache;
uoff_t cur_offset;
uint64_t cur_modseq;
int ret;
i_assert(offset <= file->sync_offset);
if (offset == file->sync_offset) {
*highest_modseq_r = file->sync_highest_modseq;
return 0;
}
cache = modseq_cache_get_offset(file, offset);
if (cache == NULL) {
/* nothing usable in cache - scan from beginning */
cur_offset = file->hdr.hdr_size;
cur_modseq = file->hdr.initial_modseq;
} else if (cache->offset == offset) {
/* exact cache hit */
*highest_modseq_r = cache->highest_modseq;
return 0;
} else {
/* use cache to skip over some records */
cur_offset = cache->offset;
cur_modseq = cache->highest_modseq;
}
ret = mail_transaction_log_file_map(file, cur_offset, offset);
if (ret <= 0) {
if (ret < 0)
return -1;
mail_index_set_error(file->log->index,
"%s: Transaction log corrupted, can't get modseq",
file->filepath);
return -1;
}
i_assert(cur_offset >= file->buffer_offset);
i_assert(cur_offset + file->buffer->used >= offset);
while (cur_offset < offset) {
if (log_get_synced_record(file, &cur_offset, &hdr) < 0)
return- 1;
mail_transaction_update_modseq(hdr, hdr + 1, &cur_modseq);
}
/* @UNSAFE: cache the value */
memmove(file->modseq_cache + 1, file->modseq_cache,
sizeof(*file->modseq_cache) *
(N_ELEMENTS(file->modseq_cache) - 1));
file->modseq_cache[0].offset = cur_offset;
file->modseq_cache[0].highest_modseq = cur_modseq;
*highest_modseq_r = cur_modseq;
return 0;
}
int mail_transaction_log_file_get_modseq_next_offset(
struct mail_transaction_log_file *file,
uint64_t modseq, uoff_t *next_offset_r)
{
const struct mail_transaction_header *hdr;
struct modseq_cache *cache;
uoff_t cur_offset;
uint64_t cur_modseq;
int ret;
if (modseq >= file->sync_highest_modseq) {
*next_offset_r = file->sync_offset;
return 0;
}
if (modseq == file->hdr.initial_modseq) {
*next_offset_r = file->hdr.hdr_size;
return 0;
}
cache = modseq_cache_get_modseq(file, modseq);
if (cache == NULL) {
/* nothing usable in cache - scan from beginning */
cur_offset = file->hdr.hdr_size;
cur_modseq = file->hdr.initial_modseq;
} else if (cache->highest_modseq == modseq) {
/* exact cache hit */
*next_offset_r = cache->offset;
return 0;
} else {
/* use cache to skip over some records */
cur_offset = cache->offset;
cur_modseq = cache->highest_modseq;
}
ret = mail_transaction_log_file_map(file, cur_offset,
file->sync_offset);
if (ret <= 0) {
if (ret < 0)
return -1;
mail_index_set_error(file->log->index,
"%s: Transaction log corrupted, can't get modseq",
file->filepath);
return -1;
}
i_assert(cur_offset >= file->buffer_offset);
while (cur_offset < file->sync_offset) {
if (log_get_synced_record(file, &cur_offset, &hdr) < 0)
return -1;
mail_transaction_update_modseq(hdr, hdr + 1, &cur_modseq);
if (cur_modseq >= modseq)
break;
}
if (cur_offset == file->sync_offset) {
/* if we got to sync_offset, cur_modseq should be
sync_highest_modseq */
mail_index_set_error(file->log->index,
"%s: Transaction log changed unexpectedly, "
"can't get modseq", file->filepath);
return -1;
}
/* @UNSAFE: cache the value */
memmove(file->modseq_cache + 1, file->modseq_cache,
sizeof(*file->modseq_cache) *
(N_ELEMENTS(file->modseq_cache) - 1));
file->modseq_cache[0].offset = cur_offset;
file->modseq_cache[0].highest_modseq = cur_modseq;
*next_offset_r = cur_offset;
return 0;
}
static int
log_file_track_sync(struct mail_transaction_log_file *file,
const struct mail_transaction_header *hdr,
unsigned int trans_size)
{
const void *data = hdr + 1;
int ret;
mail_transaction_update_modseq(hdr, hdr + 1,
&file->sync_highest_modseq);
if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0)
return 0;
/* external transactions: */
switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
case MAIL_TRANSACTION_HEADER_UPDATE:
/* see if this updates mailbox_sync_offset */
ret = log_file_track_mailbox_sync_offset_hdr(file, data,
trans_size -
sizeof(*hdr));
if (ret != 0)
return ret < 0 ? -1 : 0;
break;
case MAIL_TRANSACTION_INDEX_DELETED:
if (file->sync_offset < file->index_undeleted_offset)
break;
file->log->index->index_deleted = TRUE;
file->index_deleted_offset = file->sync_offset + trans_size;
break;
case MAIL_TRANSACTION_INDEX_UNDELETED:
if (file->sync_offset < file->index_deleted_offset)
break;
file->log->index->index_deleted = FALSE;
file->log->index->index_delete_requested = FALSE;
file->index_undeleted_offset = file->sync_offset + trans_size;
break;
}
if (file->max_tail_offset == file->sync_offset) {
/* external transactions aren't synced to mailbox. we can
update mailbox sync offset to skip this transaction to
avoid re-reading it at the next sync. */
file->max_tail_offset += trans_size;
}
return 0;
}
static int
mail_transaction_log_file_sync(struct mail_transaction_log_file *file)
{
const struct mail_transaction_header *hdr;
const void *data;
struct stat st;
size_t size, avail;
uint32_t trans_size = 0;
i_assert(file->sync_offset >= file->buffer_offset);
data = buffer_get_data(file->buffer, &size);
while (file->sync_offset - file->buffer_offset + sizeof(*hdr) <= size) {
hdr = CONST_PTR_OFFSET(data, file->sync_offset -
file->buffer_offset);
trans_size = mail_index_offset_to_uint32(hdr->size);
if (trans_size == 0) {
/* unfinished */
return 1;
}
if (trans_size < sizeof(*hdr)) {
mail_transaction_log_file_set_corrupted(file,
"hdr.size too small (%u)", trans_size);
return -1;
}
if (file->sync_offset - file->buffer_offset + trans_size > size)
break;
/* transaction has been fully written */
if (log_file_track_sync(file, hdr, trans_size) < 0)
return -1;
file->sync_offset += trans_size;
trans_size = 0;
}
if (file->mmap_base != NULL && !file->locked) {
/* Now that all the mmaped pages have page faulted, check if
the file had changed while doing that. Only after the last
page has faulted, the size returned by fstat() can be
trusted. Otherwise it might point to a page boundary while
the next page is still being written.
Without this check we might see partial transactions,
sometimes causing "Extension record updated without intro
prefix" errors. */
if (fstat(file->fd, &st) < 0)
return log_file_set_syscall_error(file, "fstat()");
if ((uoff_t)st.st_size != file->last_size) {
file->last_size = st.st_size;
return 0;
}
}
avail = file->sync_offset - file->buffer_offset;
if (avail != size) {
/* There's more data than we could sync at the moment. If the
last record's size wasn't valid, we can't know if it will
be updated unless we've locked the log. */
if (trans_size != 0) {
/* pread()s or the above fstat() check for mmaps should
have guaranteed that this doesn't happen */
mail_transaction_log_file_set_corrupted(file,
"hdr.size too large (%u)", trans_size);
return -1;
} else if (file->locked) {
mail_transaction_log_file_set_corrupted(file,
"Unexpected garbage at EOF");
return -1;
}
/* The size field will be updated soon */
mail_index_flush_read_cache(file->log->index, file->filepath,
file->fd, file->locked);
}
if (file->next != NULL &&
file->hdr.file_seq == file->next->hdr.prev_file_seq &&
file->next->hdr.prev_file_offset != file->sync_offset) {
mail_transaction_log_file_set_corrupted(file,
"Invalid transaction log size "
"(%"PRIuUOFF_T" vs %u): %s", file->sync_offset,
file->log->head->hdr.prev_file_offset, file->filepath);
return -1;
}
return 1;
}
static int
mail_transaction_log_file_insert_read(struct mail_transaction_log_file *file,
uoff_t offset)
{
void *data;
size_t size;
ssize_t ret;
size = file->buffer_offset - offset;
buffer_copy(file->buffer, size, file->buffer, 0, (size_t)-1);
data = buffer_get_space_unsafe(file->buffer, 0, size);
ret = pread_full(file->fd, data, size, offset);
if (ret > 0) {
/* success */
file->buffer_offset -= size;
return 1;
}
/* failure. don't leave ourself to inconsistent state */
buffer_copy(file->buffer, 0, file->buffer, size, (size_t)-1);
buffer_set_used_size(file->buffer, file->buffer->used - size);
if (ret == 0) {
mail_transaction_log_file_set_corrupted(file, "file shrank");
return 0;
} else if (errno == ESTALE) {
/* log file was deleted in NFS server, fail silently */
return 0;
} else {
return log_file_set_syscall_error(file, "pread()");
}
}
static int
mail_transaction_log_file_read_more(struct mail_transaction_log_file *file)
{
void *data;
size_t size;
uint32_t read_offset;
ssize_t ret;
read_offset = file->buffer_offset + buffer_get_used_size(file->buffer);
do {
data = buffer_append_space_unsafe(file->buffer, LOG_PREFETCH);
ret = pread(file->fd, data, LOG_PREFETCH, read_offset);
if (ret > 0)
read_offset += ret;
size = read_offset - file->buffer_offset;
buffer_set_used_size(file->buffer, size);
} while (ret > 0 || (ret < 0 && errno == EINTR));
file->last_size = read_offset;
if (ret < 0) {
if (errno == ESTALE) {
/* log file was deleted in NFS server, fail silently */
return 0;
}
return log_file_set_syscall_error(file, "pread()");
}
return 1;
}
static bool
mail_transaction_log_file_need_nfs_flush(struct mail_transaction_log_file *file)
{
const struct mail_index_header *hdr = &file->log->index->map->hdr;
uoff_t max_offset = file->last_size;
if (file->next != NULL &&
file->hdr.file_seq == file->next->hdr.prev_file_seq &&
file->next->hdr.prev_file_offset != max_offset) {
/* we already have a newer log file which says that we haven't
synced the entire file. */
return TRUE;
}
if (file->hdr.file_seq == hdr->log_file_seq &&
max_offset < hdr->log_file_head_offset)
return TRUE;
return FALSE;
}
static int
mail_transaction_log_file_read(struct mail_transaction_log_file *file,
uoff_t start_offset, bool nfs_flush)
{
bool index_nfs_flush =
(file->log->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0;
int ret;
i_assert(file->mmap_base == NULL);
/* NFS: if file isn't locked, we're optimistic that we can read enough
data without flushing attribute cache. if after reading we notice
that we really should have read more, flush the cache and try again.
if file is locked, the attribute cache was already flushed when
refreshing the log. */
if (index_nfs_flush && nfs_flush) {
if (!file->locked)
nfs_flush_attr_cache_unlocked(file->filepath);
else {
nfs_flush_attr_cache_fd_locked(file->filepath,
file->fd);
}
}
if (file->buffer != NULL && file->buffer_offset > start_offset) {
/* we have to insert missing data to beginning of buffer */
ret = mail_transaction_log_file_insert_read(file, start_offset);
if (ret <= 0)
return ret;
}
if (file->buffer == NULL) {
file->buffer =
buffer_create_dynamic(default_pool, LOG_PREFETCH);
file->buffer_offset = start_offset;
}
if ((ret = mail_transaction_log_file_read_more(file)) <= 0)
return ret;
if (index_nfs_flush && !nfs_flush &&
mail_transaction_log_file_need_nfs_flush(file)) {
/* we didn't read enough data. flush and try again. */
return mail_transaction_log_file_read(file, start_offset, TRUE);
}
if ((ret = mail_transaction_log_file_sync(file)) <= 0) {
i_assert(ret != 0); /* happens only with mmap */
return -1;
}
i_assert(file->sync_offset >= file->buffer_offset);
buffer_set_used_size(file->buffer,
file->sync_offset - file->buffer_offset);
return 1;
}
static int
log_file_map_check_offsets(struct mail_transaction_log_file *file,
uoff_t start_offset, uoff_t end_offset)
{
if (start_offset > file->sync_offset) {
/* broken start offset */
mail_index_set_error(file->log->index,
"%s: start_offset (%"PRIuUOFF_T") > "
"current sync_offset (%"PRIuUOFF_T")",
file->filepath, start_offset, file->sync_offset);
return 0;
}
if (end_offset != (uoff_t)-1 && end_offset > file->sync_offset) {
mail_index_set_error(file->log->index,
"%s: end_offset (%"PRIuUOFF_T") > "
"current sync_offset (%"PRIuUOFF_T")",
file->filepath, start_offset, file->sync_offset);
return 0;
}
return 1;
}
static int
mail_transaction_log_file_mmap(struct mail_transaction_log_file *file)
{
if (file->buffer != NULL) {
/* in case we just switched to mmaping */
buffer_free(&file->buffer);
}
file->mmap_size = file->last_size;
file->mmap_base = mmap(NULL, file->mmap_size, PROT_READ, MAP_SHARED,
file->fd, 0);
if (file->mmap_base == MAP_FAILED) {
file->mmap_base = NULL;
file->mmap_size = 0;
return log_file_set_syscall_error(file, "mmap()");
}
if (file->mmap_size > mmap_get_page_size()) {
if (madvise(file->mmap_base, file->mmap_size,
MADV_SEQUENTIAL) < 0)
log_file_set_syscall_error(file, "madvise()");
}
buffer_create_const_data(&file->mmap_buffer,
file->mmap_base, file->mmap_size);
file->buffer = &file->mmap_buffer;
file->buffer_offset = 0;
return 0;
}
static void
mail_transaction_log_file_munmap(struct mail_transaction_log_file *file)
{
if (file->mmap_base == NULL)
return;
if (munmap(file->mmap_base, file->mmap_size) < 0)
log_file_set_syscall_error(file, "munmap()");
file->mmap_base = NULL;
file->mmap_size = 0;
buffer_free(&file->buffer);
}
static int
mail_transaction_log_file_map_mmap(struct mail_transaction_log_file *file,
uoff_t start_offset)
{
struct stat st;
int ret;
/* we are going to mmap() this file, but it's not necessarily
mmaped currently. */
i_assert(file->buffer_offset == 0 || file->mmap_base == NULL);
i_assert(file->mmap_size == 0 || file->mmap_base != NULL);
if (fstat(file->fd, &st) < 0)
return log_file_set_syscall_error(file, "fstat()");
file->last_size = st.st_size;
if ((uoff_t)st.st_size < file->sync_offset) {
mail_transaction_log_file_set_corrupted(file,
"file size shrank");
return 0;
}
if (file->buffer != NULL && file->buffer_offset <= start_offset &&
(uoff_t)st.st_size == file->buffer_offset + file->buffer->used) {
/* we already have the whole file mapped */
if ((ret = mail_transaction_log_file_sync(file)) < 0)
return 0;
if (ret > 0)
return 1;
/* size changed, re-mmap */
}
do {
mail_transaction_log_file_munmap(file);
if (file->last_size - start_offset < mmap_get_page_size()) {
/* just reading the file is probably faster */
return mail_transaction_log_file_read(file,
start_offset,
FALSE);
}
if (mail_transaction_log_file_mmap(file) < 0)
return -1;
if ((ret = mail_transaction_log_file_sync(file)) < 0)
return 0;
} while (ret == 0);
return 1;
}
int mail_transaction_log_file_map(struct mail_transaction_log_file *file,
uoff_t start_offset, uoff_t end_offset)
{
struct mail_index *index = file->log->index;
size_t size;
int ret;
if (file->hdr.indexid == 0) {
/* corrupted */
return 0;
}
i_assert(start_offset >= file->hdr.hdr_size);
i_assert(start_offset <= end_offset);
if (index->log_locked && file == file->log->head &&
end_offset == (uoff_t)-1) {
/* we're not interested of going further than sync_offset */
if (log_file_map_check_offsets(file, start_offset,
end_offset) == 0)
return 0;
i_assert(start_offset <= file->sync_offset);
end_offset = file->sync_offset;
}
if (file->buffer != NULL && file->buffer_offset <= start_offset) {
/* see if we already have it */
size = buffer_get_used_size(file->buffer);
if (file->buffer_offset + size >= end_offset)
return 1;
}
if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) {
if (start_offset < file->buffer_offset) {
/* we had moved the log to memory but failed to read
the beginning of the log file */
mail_index_set_error(index,
"%s: Beginning of the log isn't available",
file->filepath);
return 0;
}
return log_file_map_check_offsets(file, start_offset,
end_offset);
}
if (start_offset > file->sync_offset)
mail_transaction_log_file_skip_to_head(file);
if (start_offset > file->sync_offset) {
/* although we could just skip over the unwanted data, we have
to sync everything so that modseqs are calculated
correctly */
start_offset = file->sync_offset;
}
if ((file->log->flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) == 0)
ret = mail_transaction_log_file_map_mmap(file, start_offset);
else {
mail_transaction_log_file_munmap(file);
ret = mail_transaction_log_file_read(file, start_offset, FALSE);
}
return ret <= 0 ? ret :
log_file_map_check_offsets(file, start_offset, end_offset);
}
void mail_transaction_log_file_move_to_memory(struct mail_transaction_log_file
*file)
{
buffer_t *buf;
if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file))
return;
if (file->mmap_base != NULL) {
/* just copy to memory */
i_assert(file->buffer_offset == 0);
buf = buffer_create_dynamic(default_pool, file->mmap_size);
buffer_append(buf, file->mmap_base, file->mmap_size);
buffer_free(&file->buffer);
file->buffer = buf;
/* and lose the mmap */
if (munmap(file->mmap_base, file->mmap_size) < 0)
log_file_set_syscall_error(file, "munmap()");
file->mmap_base = NULL;
} else if (file->buffer_offset != 0) {
/* we don't have the full log in the memory. read it. */
(void)mail_transaction_log_file_read(file, 0, FALSE);
}
file->last_size = 0;
if (close(file->fd) < 0)
log_file_set_syscall_error(file, "close()");
file->fd = -1;
i_free(file->filepath);
file->filepath = i_strdup(file->log->filepath);
}