mail-transaction-log.c revision 5b809b97673fb0a73aa5b9d82122612d699f6c5b
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include "lib.h"
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include "ioloop.h"
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include "buffer.h"
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include "file-dotlock.h"
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include "nfs-workarounds.h"
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila#include "mmap-util.h"
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include "mail-index-private.h"
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include "mail-transaction-log-private.h"
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include <stddef.h>
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen#include <stdio.h>
07dabfd18e418c92b4de84c133f105835779bebeTimo Sirainen#include <sys/stat.h>
07dabfd18e418c92b4de84c133f105835779bebeTimo Sirainen
07dabfd18e418c92b4de84c133f105835779bebeTimo Sirainenstatic void
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenmail_transaction_log_set_head(struct mail_transaction_log *log,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct mail_transaction_log_file *file)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_assert(log->head != file);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
60ba197d17023594231d9805d889817782e41859Timo Sirainen file->refcount++;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen log->head = file;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_assert(log->files != NULL);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_assert(log->files->next != NULL || log->files == file);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainenstruct mail_transaction_log *
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainenmail_transaction_log_alloc(struct mail_index *index)
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen{
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen struct mail_transaction_log *log;
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen log = i_new(struct mail_transaction_log, 1);
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen log->index = index;
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen return log;
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen}
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainenstatic void mail_transaction_log_2_unlink_old(struct mail_transaction_log *log)
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen{
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen struct stat st;
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(log->index))
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen return;
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen if (stat(log->filepath2, &st) < 0) {
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen if (errno != ENOENT && errno != ESTALE) {
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen mail_index_set_error(log->index,
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen "stat(%s) failed: %m", log->filepath2);
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen }
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen return;
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen }
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen if (ioloop_time - st.st_mtime >= (time_t)log->index->log_rotate_log2_stale_secs &&
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen !log->index->readonly)
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen i_unlink_if_exists(log->filepath2);
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenint mail_transaction_log_open(struct mail_transaction_log *log)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct mail_transaction_log_file *file;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen const char *reason;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen int ret;
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_free(log->filepath);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_free(log->filepath2);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen log->filepath = i_strconcat(log->index->filepath,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen MAIL_TRANSACTION_LOG_SUFFIX, NULL);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen log->filepath2 = i_strconcat(log->filepath, ".2", NULL);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen /* these settings aren't available at alloc() time, so we need to
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen set them here: */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen log->nfs_flush =
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen (log->index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0;
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (log->open_file != NULL)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen mail_transaction_log_file_free(&log->open_file);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(log->index))
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return 0;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen file = mail_transaction_log_file_alloc(log, log->filepath);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if ((ret = mail_transaction_log_file_open(file, &reason)) <= 0) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen /* leave the file for _create() */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen log->open_file = file;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return ret;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen mail_transaction_log_set_head(log, file);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return 1;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
4c61d6b6ad6173f06563c2cee9bd813c59277dd2Timo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainenint mail_transaction_log_create(struct mail_transaction_log *log, bool reset)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct mail_transaction_log_file *file;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
57dc9669d34de7d08a44be9d7d3c9f6a0c34cc87Timo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(log->index)) {
57dc9669d34de7d08a44be9d7d3c9f6a0c34cc87Timo Sirainen file = mail_transaction_log_file_alloc_in_memory(log);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen mail_transaction_log_set_head(log, file);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return 0;
57dc9669d34de7d08a44be9d7d3c9f6a0c34cc87Timo Sirainen }
57dc9669d34de7d08a44be9d7d3c9f6a0c34cc87Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen file = mail_transaction_log_file_alloc(log, log->filepath);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (log->open_file != NULL) {
07dabfd18e418c92b4de84c133f105835779bebeTimo Sirainen /* remember what file we tried to open. if someone else created
07dabfd18e418c92b4de84c133f105835779bebeTimo Sirainen a new file, use it instead of recreating it */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen file->st_ino = log->open_file->st_ino;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen file->st_dev = log->open_file->st_dev;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen file->last_size = log->open_file->last_size;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen file->last_mtime = log->open_file->last_mtime;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen mail_transaction_log_file_free(&log->open_file);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (mail_transaction_log_file_create(file, reset) < 0) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen mail_transaction_log_file_free(&file);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return -1;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen mail_transaction_log_set_head(log, file);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return 1;
4c61d6b6ad6173f06563c2cee9bd813c59277dd2Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
4c61d6b6ad6173f06563c2cee9bd813c59277dd2Timo Sirainenvoid mail_transaction_log_close(struct mail_transaction_log *log)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_assert(log->views == NULL);
4c61d6b6ad6173f06563c2cee9bd813c59277dd2Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (log->open_file != NULL)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen mail_transaction_log_file_free(&log->open_file);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (log->head != NULL)
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen log->head->refcount--;
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen mail_transaction_logs_clean(log);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_assert(log->files == NULL);
fac865bad1ba10e85d80b63dedfd3493a65510d4Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenvoid mail_transaction_log_free(struct mail_transaction_log **_log)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen struct mail_transaction_log *log = *_log;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen *_log = NULL;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen mail_transaction_log_close(log);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen log->index->log = NULL;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_free(log->filepath);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_free(log->filepath2);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_free(log);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen}
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainenint mail_transaction_log_move_to_memory(struct mail_transaction_log *log)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen{
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila struct mail_transaction_log_file *file;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila if (!log->index->initial_mapped && log->files != NULL &&
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila log->files->hdr.prev_file_seq != 0) {
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila /* we couldn't read dovecot.index and we don't have the first
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila .log file, so just start from scratch */
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila mail_transaction_log_close(log);
957b0b4c9aeff7153bb9ebf91d8aea550bd07865Teemu Huovila }
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila i_free(log->filepath);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila i_free(log->filepath2);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila log->filepath = i_strconcat(log->index->filepath,
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila MAIL_TRANSACTION_LOG_SUFFIX, NULL);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila log->filepath2 = i_strconcat(log->filepath, ".2", NULL);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
07dabfd18e418c92b4de84c133f105835779bebeTimo Sirainen if (log->head != NULL)
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila return mail_transaction_log_file_move_to_memory(log->head);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila else {
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila file = mail_transaction_log_file_alloc_in_memory(log);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila mail_transaction_log_set_head(log, file);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila return 0;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila }
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila}
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovilavoid mail_transaction_log_indexid_changed(struct mail_transaction_log *log)
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila{
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila struct mail_transaction_log_file *file;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
957b0b4c9aeff7153bb9ebf91d8aea550bd07865Teemu Huovila mail_transaction_logs_clean(log);
957b0b4c9aeff7153bb9ebf91d8aea550bd07865Teemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila for (file = log->files; file != NULL; file = file->next) {
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila if (file->hdr.indexid != log->index->indexid) {
0c827d2094e80ede4c089fc00260d7ffcc764636Timo Sirainen mail_transaction_log_file_set_corrupted(file,
0c827d2094e80ede4c089fc00260d7ffcc764636Timo Sirainen "indexid changed: %u -> %u",
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen file->hdr.indexid, log->index->indexid);
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen }
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila }
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen if (log->head != NULL &&
0ebeb1831a56e020b0958ed1ced50e86ee9347ecTimo Sirainen log->head->hdr.indexid != log->index->indexid) {
fac865bad1ba10e85d80b63dedfd3493a65510d4Timo Sirainen if (--log->head->refcount == 0)
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila mail_transaction_log_file_free(&log->head);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila (void)mail_transaction_log_create(log, FALSE);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila }
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila}
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovilavoid mail_transaction_logs_clean(struct mail_transaction_log *log)
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila{
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila struct mail_transaction_log_file *file, *next;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila /* remove only files from the beginning. this way if a view has
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila referenced an old file, it can still find the new files even if
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila there aren't any references to it currently. */
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila for (file = log->files; file != NULL; file = next) {
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila next = file->next;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila i_assert(file->refcount >= 0);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila if (file->refcount > 0)
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila break;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila mail_transaction_log_file_free(&file);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila }
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila /* sanity check: we shouldn't have locked refcount=0 files */
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila for (; file != NULL; file = file->next) {
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila i_assert(!file->locked || file->refcount > 0);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila }
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila i_assert(log->head == NULL || log->files != NULL);
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila}
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovilabool mail_transaction_log_want_rotate(struct mail_transaction_log *log)
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila{
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila struct mail_transaction_log_file *file = log->head;
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila if (file->hdr.major_version < MAIL_TRANSACTION_LOG_MAJOR_VERSION ||
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen (file->hdr.major_version == MAIL_TRANSACTION_LOG_MAJOR_VERSION &&
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen file->hdr.minor_version < MAIL_TRANSACTION_LOG_MINOR_VERSION)) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen /* upgrade immediately to a new log file format */
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila return TRUE;
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (file->sync_offset > log->index->log_rotate_max_size) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen /* file is too large, definitely rotate */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return TRUE;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (file->sync_offset < log->index->log_rotate_min_size) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen /* file is still too small */
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila return FALSE;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila }
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila /* rotate if the timestamp is old enough */
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila return file->hdr.create_stamp <
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila ioloop_time - log->index->log_rotate_min_created_ago_secs;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila}
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovilaint mail_transaction_log_rotate(struct mail_transaction_log *log, bool reset)
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila{
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila struct mail_transaction_log_file *file;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila const char *path = log->head->filepath;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila struct stat st;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila int ret;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila i_assert(log->head->locked);
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila if (MAIL_INDEX_IS_IN_MEMORY(log->index)) {
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila file = mail_transaction_log_file_alloc_in_memory(log);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (reset) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen file->hdr.prev_file_seq = 0;
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila file->hdr.prev_file_offset = 0;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen } else {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen /* we're locked, we shouldn't need to worry about ESTALE
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen problems in here. */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (fstat(log->head->fd, &st) < 0) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen mail_index_file_set_syscall_error(log->index,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen log->head->filepath, "fstat()");
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return -1;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen file = mail_transaction_log_file_alloc(log, path);
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila file->st_dev = st.st_dev;
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila file->st_ino = st.st_ino;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen file->last_mtime = st.st_mtime;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen file->last_size = st.st_size;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if ((ret = mail_transaction_log_file_create(file, reset)) < 0) {
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila mail_transaction_log_file_free(&file);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return -1;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (ret == 0) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen mail_index_set_error(log->index,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen "Transaction log %s was recreated while we had it locked - "
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen "locking is broken (lock_method=%s)", path,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen file_lock_method_to_str(log->index->lock_method));
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen mail_transaction_log_file_free(&file);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return -1;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_assert(file->locked);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (--log->head->refcount == 0)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen mail_transaction_logs_clean(log);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen else {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen /* the newly created log file is already locked */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen mail_transaction_log_file_unlock(log->head,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen !log->index->log_sync_locked ? "rotating" :
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen "rotating while syncing");
60ba197d17023594231d9805d889817782e41859Timo Sirainen }
60ba197d17023594231d9805d889817782e41859Timo Sirainen mail_transaction_log_set_head(log, file);
60ba197d17023594231d9805d889817782e41859Timo Sirainen return 0;
60ba197d17023594231d9805d889817782e41859Timo Sirainen}
60ba197d17023594231d9805d889817782e41859Timo Sirainen
60ba197d17023594231d9805d889817782e41859Timo Sirainenstatic int
60ba197d17023594231d9805d889817782e41859Timo Sirainenmail_transaction_log_refresh(struct mail_transaction_log *log, bool nfs_flush,
60ba197d17023594231d9805d889817782e41859Timo Sirainen const char **reason_r)
60ba197d17023594231d9805d889817782e41859Timo Sirainen{
60ba197d17023594231d9805d889817782e41859Timo Sirainen struct mail_transaction_log_file *file;
60ba197d17023594231d9805d889817782e41859Timo Sirainen struct stat st;
60ba197d17023594231d9805d889817782e41859Timo Sirainen
60ba197d17023594231d9805d889817782e41859Timo Sirainen i_assert(log->head != NULL);
60ba197d17023594231d9805d889817782e41859Timo Sirainen
60ba197d17023594231d9805d889817782e41859Timo Sirainen if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(log->head)) {
60ba197d17023594231d9805d889817782e41859Timo Sirainen *reason_r = "Log is in memory";
60ba197d17023594231d9805d889817782e41859Timo Sirainen return 0;
60ba197d17023594231d9805d889817782e41859Timo Sirainen }
60ba197d17023594231d9805d889817782e41859Timo Sirainen
60ba197d17023594231d9805d889817782e41859Timo Sirainen if (nfs_flush && log->nfs_flush)
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen nfs_flush_file_handle_cache(log->filepath);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (nfs_safe_stat(log->filepath, &st) < 0) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (errno != ENOENT) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen mail_index_file_set_syscall_error(log->index,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen log->filepath,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen "stat()");
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen *reason_r = t_strdup_printf("stat(%s) failed: %m", log->filepath);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return -1;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen /* see if the whole directory got deleted */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (nfs_safe_stat(log->index->dir, &st) < 0 &&
60ba197d17023594231d9805d889817782e41859Timo Sirainen errno == ENOENT) {
60ba197d17023594231d9805d889817782e41859Timo Sirainen log->index->index_deleted = TRUE;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila *reason_r = "Index directory was deleted";
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila return -1;
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila }
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila /* the file should always exist at this point. if it doesn't,
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen someone deleted it manually while the index was open. try to
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen handle this nicely by creating a new log file. */
eeb03434472130f9631f164440566fd8d09e6380Timo Sirainen file = log->head;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen if (mail_transaction_log_create(log, FALSE) < 0) {
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen *reason_r = "Failed to create log";
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen return -1;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen i_assert(file->refcount > 0);
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen file->refcount--;
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen log->index->need_recreate = TRUE;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen *reason_r = "Log created";
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen return 0;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen } else if (log->head->st_ino == st.st_ino &&
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila CMP_DEV_T(log->head->st_dev, st.st_dev)) {
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila /* NFS: log files get rotated to .log.2 files instead
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila of being unlinked, so we don't bother checking if
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila the existing file has already been unlinked here
09aed882b99e865ff6d7140ae2f77a42c9e7d1a7Teemu Huovila (in which case inodes could match but point to
4ef1f9f3293965734e6e3c38c191ceb2246a721fTeemu Huovila different files) */
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen *reason_r = "Log inode is unchanged";
4ba3717a04823875c2a1d60ff9dc8177ae033d12Timo Sirainen return 0;
1537d20b852cbbf0d6971790b84e0cce5ca61307Timo Sirainen }
eeb03434472130f9631f164440566fd8d09e6380Timo Sirainen
eeb03434472130f9631f164440566fd8d09e6380Timo Sirainen file = mail_transaction_log_file_alloc(log, log->filepath);
eeb03434472130f9631f164440566fd8d09e6380Timo Sirainen if (mail_transaction_log_file_open(file, reason_r) <= 0) {
eeb03434472130f9631f164440566fd8d09e6380Timo Sirainen mail_transaction_log_file_free(&file);
eeb03434472130f9631f164440566fd8d09e6380Timo Sirainen return -1;
afbd17685101cc2bbd0614e85558bb167ab15096Timo Sirainen }
afbd17685101cc2bbd0614e85558bb167ab15096Timo Sirainen
eeb03434472130f9631f164440566fd8d09e6380Timo Sirainen i_assert(!file->locked);
if (--log->head->refcount == 0)
mail_transaction_logs_clean(log);
mail_transaction_log_set_head(log, file);
*reason_r = "Log reopened";
return 0;
}
void mail_transaction_log_get_mailbox_sync_pos(struct mail_transaction_log *log,
uint32_t *file_seq_r,
uoff_t *file_offset_r)
{
*file_seq_r = log->head->hdr.file_seq;
*file_offset_r = log->head->max_tail_offset;
}
void mail_transaction_log_set_mailbox_sync_pos(struct mail_transaction_log *log,
uint32_t file_seq,
uoff_t file_offset)
{
i_assert(file_seq == log->head->hdr.file_seq);
i_assert(file_offset >= log->head->saved_tail_offset);
if (file_offset >= log->head->max_tail_offset)
log->head->max_tail_offset = file_offset;
}
int mail_transaction_log_find_file(struct mail_transaction_log *log,
uint32_t file_seq, bool nfs_flush,
struct mail_transaction_log_file **file_r,
const char **reason_r)
{
struct mail_transaction_log_file *file;
const char *reason;
int ret;
if (file_seq > log->head->hdr.file_seq) {
/* see if the .log file has been recreated */
if (log->head->locked) {
/* transaction log is locked. there's no way a newer
file exists. */
*reason_r = "Log is locked - newer log can't exist";
return 0;
}
if (mail_transaction_log_refresh(log, FALSE, &reason) < 0) {
*reason_r = reason;
return -1;
}
if (file_seq > log->head->hdr.file_seq) {
if (!nfs_flush || !log->nfs_flush) {
*reason_r = t_strdup_printf(
"Requested newer log than exists: %s", reason);
return 0;
}
/* try again, this time flush attribute cache */
if (mail_transaction_log_refresh(log, TRUE, &reason) < 0) {
*reason_r = t_strdup_printf(
"Log refresh with NFS flush failed: %s", reason);
return -1;
}
if (file_seq > log->head->hdr.file_seq) {
*reason_r = t_strdup_printf(
"Requested newer log than exists - "
"still after NFS flush: %s", reason);
return 0;
}
}
}
for (file = log->files; file != NULL; file = file->next) {
if (file->hdr.file_seq == file_seq) {
*file_r = file;
return 1;
}
}
if (MAIL_INDEX_IS_IN_MEMORY(log->index)) {
*reason_r = "Logs are only in memory";
return 0;
}
/* see if we have it in log.2 file */
file = mail_transaction_log_file_alloc(log, log->filepath2);
if ((ret = mail_transaction_log_file_open(file, reason_r)) <= 0) {
mail_transaction_log_file_free(&file);
return ret;
}
/* but is it what we expected? */
if (file->hdr.file_seq != file_seq) {
*reason_r = t_strdup_printf(".log.2 contains file_seq=%u",
file->hdr.file_seq);
return 0;
}
*file_r = file;
return 1;
}
int mail_transaction_log_lock_head(struct mail_transaction_log *log,
const char *lock_reason)
{
struct mail_transaction_log_file *file;
time_t lock_wait_started, lock_secs = 0;
const char *reason;
int ret = 0;
if (!log->log_2_unlink_checked) {
/* we need to check once in a while if .log.2 should be deleted
to avoid wasting space on such old files. but we also don't
want to waste time on checking it when the same mailbox
gets opened over and over again rapidly (e.g. pop3). so
do this only when there have actually been some changes
to mailbox (i.e. when it's being locked here) */
log->log_2_unlink_checked = TRUE;
mail_transaction_log_2_unlink_old(log);
}
/* we want to get the head file locked. this is a bit racy,
since by the time we have it locked a new log file may have been
created.
creating new log file requires locking the head file, so if we
can lock it and don't see another file, we can be sure no-one is
creating a new log at the moment */
lock_wait_started = time(NULL);
for (;;) {
file = log->head;
if (mail_transaction_log_file_lock(file) < 0)
return -1;
file->refcount++;
ret = mail_transaction_log_refresh(log, TRUE, &reason);
if (--file->refcount == 0) {
mail_transaction_log_file_unlock(file, t_strdup_printf(
"trying to lock head for %s", lock_reason));
mail_transaction_logs_clean(log);
file = NULL;
}
if (ret == 0 && log->head == file) {
/* success */
i_assert(file != NULL);
lock_secs = file->lock_created - lock_wait_started;
break;
}
if (file != NULL) {
mail_transaction_log_file_unlock(file, t_strdup_printf(
"trying to lock head for %s", lock_reason));
}
if (ret < 0)
break;
/* try again */
}
if (lock_secs > MAIL_TRANSACTION_LOG_LOCK_WARN_SECS) {
i_warning("Locking transaction log file %s took %ld seconds (%s)",
log->head->filepath, (long)lock_secs, lock_reason);
}
i_assert(ret < 0 || log->head != NULL);
return ret;
}
int mail_transaction_log_sync_lock(struct mail_transaction_log *log,
const char *lock_reason,
uint32_t *file_seq_r, uoff_t *file_offset_r)
{
const char *reason;
i_assert(!log->index->log_sync_locked);
if (mail_transaction_log_lock_head(log, lock_reason) < 0)
return -1;
/* update sync_offset */
if (mail_transaction_log_file_map(log->head, log->head->sync_offset,
(uoff_t)-1, &reason) <= 0) {
mail_index_set_error(log->index,
"Failed to map transaction log %s at "
"sync_offset=%"PRIuUOFF_T" after locking: %s",
log->head->filepath, log->head->sync_offset, reason);
mail_transaction_log_file_unlock(log->head, t_strdup_printf(
"%s - map failed", lock_reason));
return -1;
}
log->index->log_sync_locked = TRUE;
*file_seq_r = log->head->hdr.file_seq;
*file_offset_r = log->head->sync_offset;
return 0;
}
void mail_transaction_log_sync_unlock(struct mail_transaction_log *log,
const char *lock_reason)
{
i_assert(log->index->log_sync_locked);
log->index->log_sync_locked = FALSE;
mail_transaction_log_file_unlock(log->head, lock_reason);
}
void mail_transaction_log_get_head(struct mail_transaction_log *log,
uint32_t *file_seq_r, uoff_t *file_offset_r)
{
*file_seq_r = log->head->hdr.file_seq;
*file_offset_r = log->head->sync_offset;
}
void mail_transaction_log_get_tail(struct mail_transaction_log *log,
uint32_t *file_seq_r)
{
struct mail_transaction_log_file *tail, *file = log->files;
for (tail = file; file->next != NULL; file = file->next) {
if (file->hdr.file_seq + 1 != file->next->hdr.file_seq)
tail = file->next;
}
*file_seq_r = tail->hdr.file_seq;
}
bool mail_transaction_log_is_head_prev(struct mail_transaction_log *log,
uint32_t file_seq, uoff_t file_offset)
{
return log->head->hdr.prev_file_seq == file_seq &&
log->head->hdr.prev_file_offset == file_offset;
}
int mail_transaction_log_get_mtime(struct mail_transaction_log *log,
time_t *mtime_r)
{
struct stat st;
*mtime_r = 0;
if (stat(log->filepath, &st) < 0) {
if (errno == ENOENT)
return 0;
mail_index_file_set_syscall_error(log->index, log->filepath,
"stat()");
return -1;
}
*mtime_r = st.st_mtime;
return 0;
}
int mail_transaction_log_unlink(struct mail_transaction_log *log)
{
if (unlink(log->filepath) < 0 &&
errno != ENOENT && errno != ESTALE) {
mail_index_file_set_syscall_error(log->index, log->filepath,
"unlink()");
return -1;
}
return 0;
}
void mail_transaction_log_get_dotlock_set(struct mail_transaction_log *log,
struct dotlock_settings *set_r)
{
struct mail_index *index = log->index;
i_zero(set_r);
set_r->timeout = I_MIN(MAIL_TRANSACTION_LOG_LOCK_TIMEOUT,
index->max_lock_timeout_secs);
set_r->stale_timeout = MAIL_TRANSACTION_LOG_LOCK_CHANGE_TIMEOUT;
set_r->nfs_flush = (index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0;
set_r->use_excl_lock =
(index->flags & MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL) != 0;
}