mail-transaction-log-file.c revision 95a1a5195d56f3cf5d1e529aad668f87ad3b979b
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2003-2008 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen#include "ioloop.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "buffer.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "file-dotlock.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "nfs-workarounds.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "read-full.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "write-full.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mmap-util.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mail-index-private.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mail-index-modseq.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mail-transaction-log-private.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define LOG_PREFETCH 1024
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define MEMORY_LOG_NAME "(in-memory transaction log file)"
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainenvoid
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainenmail_transaction_log_file_set_corrupted(struct mail_transaction_log_file *file,
213b139965e8bde6c8aff02ffd9fd39a74c887a9Timo Sirainen const char *fmt, ...)
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen{
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen va_list va;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen file->corrupted = TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen file->hdr.indexid = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* indexid=0 marks the log file as corrupted */
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (pwrite_full(file->fd, &file->hdr.indexid,
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen sizeof(file->hdr.indexid),
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen offsetof(struct mail_transaction_log_header,
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen indexid)) < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen file->filepath, "pwrite()");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen va_start(va, fmt);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen T_BEGIN {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_set_error(file->log->index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Corrupted transaction log file %s: %s",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen file->filepath, t_strdup_vprintf(fmt, va));
7888a9d2008eab9985096c46e1da9ee985c22a2aTimo Sirainen } T_END;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen va_end(va);
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen}
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen
213b139965e8bde6c8aff02ffd9fd39a74c887a9Timo Sirainenstruct mail_transaction_log_file *
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainenmail_transaction_log_file_alloc(struct mail_transaction_log *log,
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen const char *path)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_transaction_log_file *file;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen file = i_new(struct mail_transaction_log_file, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen file->log = log;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen file->filepath = i_strdup(path);
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen file->fd = -1;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen return file;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen}
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainenvoid mail_transaction_log_file_free(struct mail_transaction_log_file **_file)
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen{
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen struct mail_transaction_log_file *file = *_file;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen struct mail_transaction_log_file **p;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen int old_errno = errno;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen *_file = NULL;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen mail_transaction_log_file_unlock(file);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen for (p = &file->log->files; *p != NULL; p = &(*p)->next) {
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen if (*p == file) {
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen *p = file->next;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen break;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen }
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen }
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen if (file == file->log->head)
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen file->log->head = NULL;
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen if (file->buffer != NULL)
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen buffer_free(&file->buffer);
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen if (file->mmap_base != NULL) {
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen if (munmap(file->mmap_base, file->mmap_size) < 0) {
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen file->filepath,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "munmap()");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen }
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen if (file->fd != -1) {
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen if (close(file->fd) < 0) {
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen file->filepath,
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen "close()");
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen }
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen }
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen i_free(file->filepath);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen i_free(file);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen errno = old_errno;
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen}
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_transaction_log_file_add_to_list(struct mail_transaction_log_file *file)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen struct mail_transaction_log *log = file->log;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_transaction_log_file **p;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_index_map *map = log->index->map;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen const struct mail_index_modseq_header *modseq_hdr;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen if (map != NULL && file->hdr.file_seq == map->hdr.log_file_seq &&
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen map->hdr.log_file_head_offset != 0) {
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen /* we can get a valid log offset from index file. initialize
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen sync_offset from it so we don't have to read the whole log
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen file from beginning. */
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen uoff_t head_offset = map->hdr.log_file_head_offset;
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainen
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen modseq_hdr = mail_index_map_get_modseq_header(map);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (head_offset < file->hdr.hdr_size) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen mail_index_set_error(log->index,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen "%s: log_file_head_offset too small",
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen log->index->filepath);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen file->sync_offset = file->hdr.hdr_size;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen file->sync_highest_modseq = file->hdr.initial_modseq;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen } else if (modseq_hdr == NULL ||
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen modseq_hdr->log_seq != file->hdr.file_seq ||
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainen modseq_hdr->log_offset != head_offset) {
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainen /* highest_modseq not synced, start from beginning */
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainen file->sync_offset = file->hdr.hdr_size;
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainen file->sync_highest_modseq = file->hdr.initial_modseq;
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainen } else {
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen file->sync_offset = head_offset;
0d658231054332c3f4c04aab0422af649de89a8cTimo Sirainen file->sync_highest_modseq = modseq_hdr->highest_modseq;
45e62043058738e294f89504c319d852e25943ccTimo Sirainen }
45e62043058738e294f89504c319d852e25943ccTimo Sirainen file->saved_tail_offset = map->hdr.log_file_tail_offset;
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen } else {
45e62043058738e294f89504c319d852e25943ccTimo Sirainen file->sync_offset = file->hdr.hdr_size;
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen file->sync_highest_modseq = file->hdr.initial_modseq;
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen }
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen /* insert it to correct position */
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen for (p = &log->files; *p != NULL; p = &(*p)->next) {
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen if ((*p)->hdr.file_seq > file->hdr.file_seq)
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen break;
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen i_assert((*p)->hdr.file_seq < file->hdr.file_seq);
40ef82c46f6652412b068ebcdac7c3e74840a284Timo Sirainen }
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainen
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainen file->next = *p;
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainen *p = file;
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainen}
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainen
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainenstatic int
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainenmail_transaction_log_init_hdr(struct mail_transaction_log *log,
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainen struct mail_transaction_log_header *hdr)
8eeafcb306872435f3171e6acf5a9937aec3a175Timo Sirainen{
be18b5067f9f787179f04c49168060165ed6be08Timo Sirainen struct mail_index *index = log->index;
be18b5067f9f787179f04c49168060165ed6be08Timo Sirainen
a4d209d480d453566d331e870b8d0c99af7716c8Timo Sirainen memset(hdr, 0, sizeof(*hdr));
a4d209d480d453566d331e870b8d0c99af7716c8Timo Sirainen hdr->major_version = MAIL_TRANSACTION_LOG_MAJOR_VERSION;
a4d209d480d453566d331e870b8d0c99af7716c8Timo Sirainen hdr->minor_version = MAIL_TRANSACTION_LOG_MINOR_VERSION;
a4d209d480d453566d331e870b8d0c99af7716c8Timo Sirainen hdr->hdr_size = sizeof(struct mail_transaction_log_header);
a4d209d480d453566d331e870b8d0c99af7716c8Timo Sirainen hdr->indexid = log->index->indexid;
7360e48fe84ca34ab28e21a50717907eca69d6c8Timo Sirainen hdr->create_stamp = ioloop_time;
a4d209d480d453566d331e870b8d0c99af7716c8Timo Sirainen
a4d209d480d453566d331e870b8d0c99af7716c8Timo Sirainen if (index->fd != -1) {
a4d209d480d453566d331e870b8d0c99af7716c8Timo Sirainen /* not creating index - make sure we have latest header */
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (!index->mapping) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (mail_index_map(index,
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen MAIL_INDEX_SYNC_HANDLER_HEAD) <= 0)
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainen return -1;
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainen } else {
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainen /* if we got here from mapping, the .log file is
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainen corrupted. use whatever values we got from index
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainen file */
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainen }
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainen }
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen if (index->map != NULL) {
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen hdr->prev_file_seq = index->map->hdr.log_file_seq;
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen hdr->prev_file_offset = index->map->hdr.log_file_head_offset;
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen hdr->file_seq = index->map->hdr.log_file_seq + 1;
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen hdr->initial_modseq =
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen mail_index_map_modseq_get_highest(index->map);
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen } else {
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen hdr->file_seq = 1;
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen }
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (log->head != NULL && hdr->file_seq <= log->head->hdr.file_seq) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen /* make sure the sequence grows */
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen hdr->file_seq = log->head->hdr.file_seq+1;
ccb77e2f63626ec46e5745ef4f38baa8e8e504fcTimo Sirainen }
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen return 0;
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen}
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenstruct mail_transaction_log_file *
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenmail_transaction_log_file_alloc_in_memory(struct mail_transaction_log *log)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen{
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen struct mail_transaction_log_file *file;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen file = mail_transaction_log_file_alloc(log, MEMORY_LOG_NAME);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (mail_transaction_log_init_hdr(log, &file->hdr) < 0) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen i_free(file);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen return NULL;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen }
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen file->buffer = buffer_create_dynamic(default_pool, 4096);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen file->buffer_offset = sizeof(file->hdr);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen mail_transaction_log_file_add_to_list(file);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen return file;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen}
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenstatic int
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenmail_transaction_log_file_dotlock(struct mail_transaction_log_file *file)
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen{
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen int ret;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen if (file->log->dotlock_count > 0)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen ret = 1;
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen else {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = file_dotlock_create(&file->log->dotlock_settings,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen file->filepath, 0,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen &file->log->dotlock);
c21c33a8c98972c45349066fc76ac9e2c05013c1Timo Sirainen }
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (ret > 0) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen file->log->dotlock_count++;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen file->locked = TRUE;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen return 0;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen }
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (ret < 0) {
c21c33a8c98972c45349066fc76ac9e2c05013c1Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen file->filepath,
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen "file_dotlock_create()");
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen return -1;
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen }
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen mail_index_set_error(file->log->index,
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen "Timeout while waiting for "
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen "dotlock for transaction log file %s",
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen file->filepath);
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen file->log->index->index_lock_timeout = TRUE;
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen return -1;
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen}
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenstatic int
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenmail_transaction_log_file_undotlock(struct mail_transaction_log_file *file)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen{
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen int ret;
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen if (--file->log->dotlock_count > 0)
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen return 0;
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen ret = file_dotlock_delete(&file->log->dotlock);
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen if (ret < 0) {
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen file->filepath, "file_dotlock_delete()");
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen return -1;
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen }
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen if (ret == 0) {
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen mail_index_set_error(file->log->index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Dotlock was lost for transaction log file %s",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen file->filepath);
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen return -1;
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen}
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenint mail_transaction_log_file_lock(struct mail_transaction_log_file *file)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen{
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen int ret;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (file->locked)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen return 0;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen file->locked = TRUE;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen return 0;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen }
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (file->log->index->lock_method == FILE_LOCK_METHOD_DOTLOCK)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return mail_transaction_log_file_dotlock(file);
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen i_assert(file->file_lock == NULL);
527ed64bc924b4a13b570a8450f8be3efdf71879Timo Sirainen ret = mail_index_lock_fd(file->log->index, file->filepath, file->fd,
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen F_WRLCK, MAIL_TRANSCATION_LOG_LOCK_TIMEOUT,
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen &file->file_lock);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (ret > 0) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen file->locked = TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ret < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
1cf72a848805fcf014b01c9d3665b6a157846a21Timo Sirainen file->filepath,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "mail_index_wait_lock_fd()");
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen return -1;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen mail_index_set_error(file->log->index,
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen "Timeout while waiting for lock for transaction log file %s",
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen file->filepath);
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen file->log->index->index_lock_timeout = TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
c680a6b35b459045e92814778908da5a93922107Timo Sirainen
c680a6b35b459045e92814778908da5a93922107Timo Sirainenvoid mail_transaction_log_file_unlock(struct mail_transaction_log_file *file)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen{
c680a6b35b459045e92814778908da5a93922107Timo Sirainen if (!file->locked)
c680a6b35b459045e92814778908da5a93922107Timo Sirainen return;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
f537e7efaec891d6b3320ca94331d09ca8c4a4dbTimo Sirainen file->locked = FALSE;
c680a6b35b459045e92814778908da5a93922107Timo Sirainen
c680a6b35b459045e92814778908da5a93922107Timo Sirainen if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file))
c680a6b35b459045e92814778908da5a93922107Timo Sirainen return;
c680a6b35b459045e92814778908da5a93922107Timo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen if (file->log->index->lock_method == FILE_LOCK_METHOD_DOTLOCK) {
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen mail_transaction_log_file_undotlock(file);
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen return;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen file_unlock(&file->file_lock);
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen}
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenstatic int
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainenmail_transaction_log_file_read_hdr(struct mail_transaction_log_file *file,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen bool ignore_estale)
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen{
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen struct mail_transaction_log_file *f;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen int ret;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen i_assert(!MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (file->corrupted)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = pread_full(file->fd, &file->hdr, sizeof(file->hdr), 0);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ret < 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (errno != ESTALE || !ignore_estale) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen file->filepath,
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen "pread_full()");
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen return -1;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (ret == 0) {
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen mail_transaction_log_file_set_corrupted(file,
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen "unexpected end of file while reading header");
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen return 0;
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen }
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if (file->hdr.major_version != MAIL_TRANSACTION_LOG_MAJOR_VERSION) {
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* incompatible version - fix silently */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen return 0;
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen }
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if (file->hdr.hdr_size < MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE) {
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen mail_transaction_log_file_set_corrupted(file,
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen "Header size too small");
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen return 0;
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen }
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if (file->hdr.hdr_size < sizeof(file->hdr)) {
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* @UNSAFE: smaller than we expected - zero out the fields we
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen shouldn't have filled */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen memset(PTR_OFFSET(&file->hdr, file->hdr.hdr_size), 0,
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen sizeof(file->hdr) - file->hdr.hdr_size);
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if (file->hdr.minor_version == 0)
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen file->hdr.initial_modseq = 1;
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen }
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if (file->hdr.indexid == 0) {
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* corrupted */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen file->corrupted = TRUE;
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen mail_index_set_error(file->log->index,
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen "Transaction log file %s: marked corrupted",
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen file->filepath);
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen return 0;
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen }
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if (file->hdr.indexid != file->log->index->indexid) {
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if (file->log->index->indexid != 0 &&
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen !file->log->index->initial_create) {
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* index file was probably just rebuilt and we don't
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen know about it yet */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen mail_transaction_log_file_set_corrupted(file,
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen "indexid changed %u -> %u",
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen file->log->index->indexid, file->hdr.indexid);
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen return 0;
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen }
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* creating index file. since transaction log is created
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen first, use the indexid in it to create the main index
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen to avoid races. */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen file->log->index->indexid = file->hdr.indexid;
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen }
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* make sure we already don't have a file with the same sequence
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen opened. it shouldn't happen unless the old log file was
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen corrupted. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for (f = file->log->files; f != NULL; f = f->next) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (f->hdr.file_seq == file->hdr.file_seq) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* mark the old file corrupted. we can't safely remove
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen it from the list however, so return failure. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_transaction_log_file_set_corrupted(f,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "duplicate transaction log sequence (%u)",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen f->hdr.file_seq);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen }
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen file->sync_highest_modseq = file->hdr.initial_modseq;
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen return 1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenmail_transaction_log_file_stat(struct mail_transaction_log_file *file,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen bool ignore_estale)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct stat st;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (fstat(file->fd, &st) < 0) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if (errno != ESTALE || !ignore_estale) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen file->filepath, "fstat()");
8bae533c96e129dca8ee7b494d7de5aeb4a043a2Timo Sirainen }
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen return -1;
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen }
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen
def1cf4b4d2d7bf8dd47258a46b5079682b99251Timo Sirainen file->st_dev = st.st_dev;
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen file->st_ino = st.st_ino;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen file->last_mtime = st.st_mtime;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen file->last_size = st.st_size;
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen return 0;
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen}
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainenstatic bool
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainenmail_transaction_log_file_is_dupe(struct mail_transaction_log_file *file)
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen{
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen struct mail_transaction_log_file *tmp;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen for (tmp = file->log->files; tmp != NULL; tmp = tmp->next) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if (tmp->st_ino == file->st_ino &&
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen CMP_DEV_T(tmp->st_dev, file->st_dev))
37b805dfb45902b6b41c45482f67e6f98e08b0a3Timo Sirainen return TRUE;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen }
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen return FALSE;
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen}
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainenstatic int
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainenmail_transaction_log_file_create2(struct mail_transaction_log_file *file,
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen int new_fd, bool reset,
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen struct dotlock **dotlock)
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen{
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen struct mail_index *index = file->log->index;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen struct stat st;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen const char *path2;
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen int fd, ret;
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen bool rename_existing;
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen if (index->nfs_flush) {
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen /* although we check also mtime and file size below, it's done
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen only to fix broken log files. we don't bother flushing
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen attribute cache just for that. */
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen nfs_flush_file_handle_cache(file->filepath);
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen }
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen /* log creation is locked now - see if someone already created it.
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen note that if we're rotating, we need to keep the log locked until
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen the file has been rewritten. and because fcntl() locks are stupid,
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if we go and open()+close() the file and we had it already opened,
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen its locks are lost. so we use stat() to check if the file has been
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen recreated, although it almost never is. */
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen if (reset)
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen rename_existing = FALSE;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen else if (nfs_safe_stat(file->filepath, &st) < 0) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if (errno != ENOENT) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen mail_index_file_set_syscall_error(index, file->filepath,
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen "stat()");
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen return -1;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen }
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen rename_existing = FALSE;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen } else if (st.st_ino == file->st_ino &&
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen CMP_DEV_T(st.st_dev, file->st_dev) &&
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* inode/dev checks are enough when we're rotating the file,
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen but not when we're replacing a broken log file */
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen st.st_mtime == file->last_mtime &&
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen (uoff_t)st.st_size == file->last_size) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* no-one else recreated the file */
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen rename_existing = TRUE;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen } else {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* recreated. use the file if its header is ok */
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen fd = nfs_safe_open(file->filepath, O_RDWR);
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if (fd == -1) {
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen if (errno != ENOENT) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen mail_index_file_set_syscall_error(index,
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen file->filepath, "open()");
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen return -1;
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen }
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen } else {
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen file->fd = fd;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if (mail_transaction_log_file_read_hdr(file,
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen FALSE) > 0 &&
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen mail_transaction_log_file_stat(file, FALSE) == 0) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* yes, it was ok */
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen (void)file_dotlock_delete(dotlock);
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen mail_transaction_log_file_add_to_list(file);
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen return 0;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen }
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen file->fd = -1;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if (close(fd) < 0) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen mail_index_file_set_syscall_error(index,
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen file->filepath, "close()");
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen }
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen }
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen rename_existing = FALSE;
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen }
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen if (mail_transaction_log_init_hdr(file->log, &file->hdr) < 0)
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen return -1;
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen if (reset) {
a5b331e18b220fac557480b569b85215a1b3bd8eTimo Sirainen file->hdr.prev_file_seq = 0;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen file->hdr.prev_file_offset = 0;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen file->hdr.initial_modseq = 1;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen }
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if (write_full(new_fd, &file->hdr, sizeof(file->hdr)) < 0) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen mail_index_file_set_syscall_error(index, file->filepath,
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen "write_full()");
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen return -1;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen }
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if (index->nfs_flush) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* the header isn't important, so don't bother calling
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen fdatasync() unless NFS is used */
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen if (fdatasync(new_fd) < 0) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen mail_index_file_set_syscall_error(index, file->filepath,
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen "fdatasync()");
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen return -1;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen }
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen }
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen file->fd = new_fd;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen ret = mail_transaction_log_file_stat(file, FALSE);
a8012fea2a7315033bc467acbf46be8e7323318cTimo Sirainen
a8012fea2a7315033bc467acbf46be8e7323318cTimo Sirainen if (file->log->head != NULL && file->log->head->locked) {
a8012fea2a7315033bc467acbf46be8e7323318cTimo Sirainen /* we'll need to preserve the lock */
834b90e1f426d1e3308670e09c050bcdea546eb8Timo Sirainen if (mail_transaction_log_file_lock(file) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* if we return -1 the dotlock deletion code closes the fd */
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen file->fd = -1;
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen if (ret < 0)
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen return -1;
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* keep two log files */
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen if (rename_existing) {
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen /* rename() would be nice and easy way to do this, except then
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen there's a race condition between the rename and
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen file_dotlock_replace(). during that time the log file
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen doesn't exist, which could cause problems. */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen path2 = t_strconcat(file->filepath, ".2", NULL);
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if (unlink(path2) < 0 && errno != ENOENT) {
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen mail_index_set_error(index, "unlink(%s) failed: %m",
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen path2);
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* try to link() anyway */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen }
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if (nfs_safe_link(file->filepath, path2, FALSE) < 0 &&
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen errno != ENOENT && errno != EEXIST) {
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen mail_index_set_error(index, "link(%s, %s) failed: %m",
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen file->filepath, path2);
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* ignore the error. we don't care that much about the
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen second log file and we're going to overwrite this
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen first one. */
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen }
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen }
b7cf555b699d73f2d71de0dabc088af6a7be3627Timo Sirainen
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen if (file_dotlock_replace(dotlock,
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) <= 0)
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen return -1;
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* success */
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen file->fd = new_fd;
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen mail_transaction_log_file_add_to_list(file);
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint mail_transaction_log_file_create(struct mail_transaction_log_file *file,
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen bool reset)
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen{
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen struct mail_index *index = file->log->index;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct dotlock *dotlock;
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen mode_t old_mask;
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen int fd;
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen i_assert(!MAIL_INDEX_IS_IN_MEMORY(index));
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen /* With dotlocking we might already have path.lock created, so this
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen filename has to be different. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen old_mask = umask(index->mode ^ 0666);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen fd = file_dotlock_open(&file->log->new_dotlock_settings,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen file->filepath, 0, &dotlock);
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen umask(old_mask);
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen if (fd == -1) {
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen mail_index_file_set_syscall_error(index, file->filepath,
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen "file_dotlock_open()");
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen return -1;
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen }
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen if (index->gid != (gid_t)-1 &&
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen fchown(fd, (uid_t)-1, index->gid) < 0) {
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen mail_index_file_set_syscall_error(index, file->filepath,
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen "fchown()");
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen (void)file_dotlock_delete(&dotlock);
85144b5f0bc763de14c7d87291a90ef74ac241a2Timo Sirainen return -1;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen /* either fd gets used or the dotlock gets deleted and returned fd
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen is for the existing file */
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (mail_transaction_log_file_create2(file, fd, reset, &dotlock) < 0) {
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (dotlock != NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (void)file_dotlock_delete(&dotlock);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 0;
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint mail_transaction_log_file_open(struct mail_transaction_log_file *file,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen bool check_existing)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int i;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen bool ignore_estale;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen for (i = 0;; i++) {
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen file->fd = nfs_safe_open(file->filepath, O_RDWR);
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen if (file->fd == -1) {
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen if (errno == ENOENT)
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen return 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen file->filepath, "open()");
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen return -1;
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen }
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen ignore_estale = i < MAIL_INDEX_ESTALE_RETRY_COUNT;
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen if (mail_transaction_log_file_stat(file, ignore_estale) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = -1;
8bae533c96e129dca8ee7b494d7de5aeb4a043a2Timo Sirainen else if (check_existing &&
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen mail_transaction_log_file_is_dupe(file))
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen return 0;
5f78b33aa505b17e23cdf27b071a24e127b3db54Timo Sirainen else {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = mail_transaction_log_file_read_hdr(file,
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen ignore_estale);
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ret > 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* success */
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen break;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (ret == 0) {
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen /* corrupted */
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (unlink(file->filepath) < 0 && errno != ENOENT) {
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen mail_index_set_error(file->log->index,
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen "unlink(%s) failed: %m",
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen file->filepath);
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen return 0;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen if (errno != ESTALE ||
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen i == MAIL_INDEX_ESTALE_RETRY_COUNT) {
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen /* syscall error */
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen return -1;
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen /* ESTALE - try again */
95a1a5195d56f3cf5d1e529aad668f87ad3b979bTimo Sirainen }
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen
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 sync_offset;
i_assert(offset_size == sizeof(sync_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(&sync_offset,
CONST_PTR_OFFSET(u + 1, offset_pos - u->offset),
sizeof(sync_offset));
if (sync_offset < file->saved_tail_offset) {
mail_transaction_log_file_set_corrupted(file,
"log_file_tail_offset shrank");
return -1;
}
file->saved_tail_offset = sync_offset;
if (sync_offset > file->max_tail_offset)
file->max_tail_offset = sync_offset;
return 1;
}
return 0;
}
bool
mail_transaction_header_has_modseq(const struct mail_transaction_header *hdr)
{
switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
case MAIL_TRANSACTION_EXPUNGE | 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:
/* these changes increase modseq */
return TRUE;
}
return FALSE;
}
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");
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;
if (mail_transaction_header_has_modseq(hdr))
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, prev_offset;
uint64_t cur_modseq;
int ret;
if (modseq >= file->sync_highest_modseq) {
*next_offset_r = file->sync_offset;
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) {
prev_offset = cur_offset;
if (log_get_synced_record(file, &cur_offset, &hdr) < 0)
return -1;
if (mail_transaction_header_has_modseq(hdr)) {
if (++cur_modseq == modseq)
break;
}
}
if (modseq != cur_modseq) {
/* 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)
{
int ret;
if (mail_transaction_header_has_modseq(hdr))
file->sync_highest_modseq++;
if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0)
return 0;
/* external transactions: */
if ((hdr->type & MAIL_TRANSACTION_TYPE_MASK) ==
MAIL_TRANSACTION_HEADER_UPDATE) {
/* see if this updates mailbox_sync_offset */
ret = log_file_track_mailbox_sync_offset_hdr(file, hdr + 1,
trans_size -
sizeof(*hdr));
if (ret != 0)
return ret < 0 ? -1 : 0;
}
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) {
mail_index_file_set_syscall_error(file->log->index,
file->filepath,
"fstat()");
return -1;
}
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_index_set_error(file->log->index,
"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 {
mail_index_file_set_syscall_error(file->log->index,
file->filepath, "pread()");
return -1;
}
}
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;
}
mail_index_file_set_syscall_error(file->log->index,
file->filepath, "pread()");
return -1;
}
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)
{
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 (file->log->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 (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->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 (file->log->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;
mail_index_file_set_syscall_error(file->log->index,
file->filepath, "mmap()");
return -1;
}
if (file->mmap_size > mmap_get_page_size()) {
if (madvise(file->mmap_base, file->mmap_size,
MADV_SEQUENTIAL) < 0) {
mail_index_file_set_syscall_error(file->log->index,
file->filepath, "madvise()");
}
}
file->buffer = buffer_create_const_data(default_pool,
file->mmap_base,
file->mmap_size);
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) {
mail_index_file_set_syscall_error(file->log->index,
file->filepath, "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) {
mail_index_file_set_syscall_error(file->log->index,
file->filepath, "fstat()");
return -1;
}
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 (!index->mmap_disable)
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) {
mail_index_file_set_syscall_error(file->log->index,
file->filepath,
"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);
}
if (close(file->fd) < 0) {
mail_index_file_set_syscall_error(file->log->index,
file->filepath, "close()");
}
file->fd = -1;
}