mail-transaction-log.c revision 8dfabfbc7715df1c9e2016f6a3f2db575265524b
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2003-2016 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen#include "lib.h"
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen#include "ioloop.h"
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen#include "buffer.h"
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen#include "file-dotlock.h"
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen#include "nfs-workarounds.h"
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen#include "mmap-util.h"
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen#include "mail-index-private.h"
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen#include "mail-transaction-log-private.h"
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainen#include <stddef.h>
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainen#include <stdio.h>
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainen#include <sys/stat.h>
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainen
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainenstatic void
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainenmail_transaction_log_set_head(struct mail_transaction_log *log,
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen struct mail_transaction_log_file *file)
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen{
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainen i_assert(log->head != file);
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainen
3c9783956dea385b322cd7fa6bf8c98c17a907a0Timo Sirainen file->refcount++;
3c9783956dea385b322cd7fa6bf8c98c17a907a0Timo Sirainen log->head = file;
3c9783956dea385b322cd7fa6bf8c98c17a907a0Timo Sirainen
3c9783956dea385b322cd7fa6bf8c98c17a907a0Timo Sirainen i_assert(log->files != NULL);
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen i_assert(log->files->next != NULL || log->files == file);
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen}
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainenstruct mail_transaction_log *
615c4aba4cc0b17eefba3263b85972adaba04586Timo Sirainenmail_transaction_log_alloc(struct mail_index *index)
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen{
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen struct mail_transaction_log *log;
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen log = i_new(struct mail_transaction_log, 1);
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen log->index = index;
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen return log;
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen}
06e56a69f41fa7efd131179f2abfeabe825ac25fTimo Sirainen
06e56a69f41fa7efd131179f2abfeabe825ac25fTimo Sirainenstatic void mail_transaction_log_2_unlink_old(struct mail_transaction_log *log)
f3ff5f0a60bf30a59ec3c2c2515c8a85b157dc09Timo Sirainen{
f3ff5f0a60bf30a59ec3c2c2515c8a85b157dc09Timo Sirainen struct stat st;
06e56a69f41fa7efd131179f2abfeabe825ac25fTimo Sirainen
d24a5c09373ec890da5736882c1756aa7fa651ebTimo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(log->index))
d24a5c09373ec890da5736882c1756aa7fa651ebTimo Sirainen return;
d24a5c09373ec890da5736882c1756aa7fa651ebTimo Sirainen
446e518e4fe86ff40e33543445f4e99edf840a21Timo Sirainen if (stat(log->filepath2, &st) < 0) {
446e518e4fe86ff40e33543445f4e99edf840a21Timo Sirainen if (errno != ENOENT && errno != ESTALE) {
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen mail_index_set_error(log->index,
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen "stat(%s) failed: %m", log->filepath2);
dc8beee89ba9409c1d70ecdfe6ad6b35635b8778Timo Sirainen }
dc8beee89ba9409c1d70ecdfe6ad6b35635b8778Timo Sirainen return;
dc8beee89ba9409c1d70ecdfe6ad6b35635b8778Timo Sirainen }
137ea7ca34005345aa2304a940149b7f3774d727Timo Sirainen
9f82881b46c5b00889dabe945872556660b5d45cTimo Sirainen if (st.st_mtime + MAIL_TRANSACTION_LOG2_STALE_SECS <= ioloop_time &&
9f82881b46c5b00889dabe945872556660b5d45cTimo Sirainen !log->index->readonly)
9f82881b46c5b00889dabe945872556660b5d45cTimo Sirainen i_unlink_if_exists(log->filepath2);
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen}
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen
7d102c66eb1755e1894cf56e3594cd744e855238Timo Sirainenint mail_transaction_log_open(struct mail_transaction_log *log)
190d84a496d6893ed54fe5bdb9e503285583d13fTimo Sirainen{
190d84a496d6893ed54fe5bdb9e503285583d13fTimo Sirainen struct mail_transaction_log_file *file;
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen int ret;
7242e1ce7803b83bc82e239ef111b47c1c72dd4bAndrey Panin
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen i_free(log->filepath);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen i_free(log->filepath2);
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen log->filepath = i_strconcat(log->index->filepath,
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen MAIL_TRANSACTION_LOG_SUFFIX, NULL);
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen log->filepath2 = i_strconcat(log->filepath, ".2", NULL);
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen /* these settings aren't available at alloc() time, so we need to
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen set them here: */
657afb33796f8216c568ad813627da89970760beTimo Sirainen log->nfs_flush =
657afb33796f8216c568ad813627da89970760beTimo Sirainen (log->index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0;
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen
fda2c460c58f50bf035680187be606542a8c1dacTimo Sirainen if (log->open_file != NULL)
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch mail_transaction_log_file_free(&log->open_file);
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen
f016dec9837e6a41867708e4b89ca5308dedab05Timo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(log->index))
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen return 0;
9c3577aeb78a27920439ad9f1e62ee03699378c3Timo Sirainen
657afb33796f8216c568ad813627da89970760beTimo Sirainen file = mail_transaction_log_file_alloc(log, log->filepath);
48ac75465ae154b1d705f18de6d95045ab714b65Timo Sirainen if ((ret = mail_transaction_log_file_open(file)) <= 0) {
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen /* leave the file for _create() */
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen log->open_file = file;
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen return ret;
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen }
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen mail_transaction_log_set_head(log, file);
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen return 1;
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen}
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainenint mail_transaction_log_create(struct mail_transaction_log *log, bool reset)
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen{
a8e132559a7ebe54c8269d79ce29fa3338c76199Timo Sirainen struct mail_transaction_log_file *file;
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen
657afb33796f8216c568ad813627da89970760beTimo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(log->index)) {
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen file = mail_transaction_log_file_alloc_in_memory(log);
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen mail_transaction_log_set_head(log, file);
55ff327f2806a10aae4bcc4f5f15d130449ca3e2Timo Sirainen return 0;
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen }
bc2739c2649a2168c4c47501d014c6dd93f6e74eTimo Sirainen
15e1aa97d4b3d74f58e291e2edaadc2fa1564c43Timo Sirainen file = mail_transaction_log_file_alloc(log, log->filepath);
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen if (log->open_file != NULL) {
657afb33796f8216c568ad813627da89970760beTimo Sirainen /* remember what file we tried to open. if someone else created
e31bf6003e580bcb28af333119da9dd1e16fc811Timo Sirainen a new file, use it instead of recreating it */
446e518e4fe86ff40e33543445f4e99edf840a21Timo Sirainen file->st_ino = log->open_file->st_ino;
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen file->st_dev = log->open_file->st_dev;
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen file->last_size = log->open_file->last_size;
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen file->last_mtime = log->open_file->last_mtime;
ce89e2964b6bc4925d2dd690417200a110d041c5Timo Sirainen mail_transaction_log_file_free(&log->open_file);
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen }
430c0b0c370bebeeceba2e206be76bc134742f41Timo Sirainen
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen if (mail_transaction_log_file_create(file, reset) < 0) {
3d8f3c378de13e32018e2b116f6b67bd69cd28fbTimo Sirainen mail_transaction_log_file_free(&file);
113fdc66ca01c407b0e3bd604c0857be8647a493Timo Sirainen return -1;
8be0af2dd41283fdd250b9a0d8fad28bfe80ca85Timo Sirainen }
258ff7d4f03dd9d29eca3664e4acacdf7f528234Timo Sirainen
d8702d15ee7721ed1fcfc8f00a589970bd6b3598Timo Sirainen mail_transaction_log_set_head(log, file);
939451389b8e0ad529277b84fe51dab38a8cf77cTimo Sirainen return 1;
c5383a0ed56a188a7d5efaaa4c6f8243af432d65Timo Sirainen}
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen
20230315dc9cd159c41ba76d24557807b6ad5a61Timo Sirainenvoid mail_transaction_log_close(struct mail_transaction_log *log)
723b179c91478cfacd187d1743a029d103f93547Timo Sirainen{
ea95a057fa5f02d50027122cacd3147fce7679faTimo Sirainen i_assert(log->views == NULL);
939451389b8e0ad529277b84fe51dab38a8cf77cTimo Sirainen
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen if (log->open_file != NULL)
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen mail_transaction_log_file_free(&log->open_file);
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen if (log->head != NULL)
8be0af2dd41283fdd250b9a0d8fad28bfe80ca85Timo Sirainen log->head->refcount--;
8be0af2dd41283fdd250b9a0d8fad28bfe80ca85Timo Sirainen mail_transaction_logs_clean(log);
8a8c23ec3de98ef55fd681514587a8c9e082777aTimo Sirainen i_assert(log->files == NULL);
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen}
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainenvoid mail_transaction_log_free(struct mail_transaction_log **_log)
39dcbe101c4538ee25d8b196eab30e5f0faa22eeTimo Sirainen{
f2e7603c5d53b3dda364e93dd926d74d6047cb25Timo Sirainen struct mail_transaction_log *log = *_log;
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen
4ac5448461b63de9637de839fbc611a3d503287cTimo Sirainen *_log = NULL;
50782de8a9d5ebe11ee61496b4e695a1d3875230Timo Sirainen
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen mail_transaction_log_close(log);
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen log->index->log = NULL;
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen i_free(log->filepath);
9ed2951bd0bb1878a27437d7c00611b2baadd614Timo Sirainen i_free(log->filepath2);
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen i_free(log);
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen}
401b0787fff2dc986a5321ddb32acb1947ff66b0Timo Sirainen
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainenvoid mail_transaction_log_move_to_memory(struct mail_transaction_log *log)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen{
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen struct mail_transaction_log_file *file;
657afb33796f8216c568ad813627da89970760beTimo Sirainen
657afb33796f8216c568ad813627da89970760beTimo Sirainen if (!log->index->initial_mapped && log->files != NULL &&
657afb33796f8216c568ad813627da89970760beTimo Sirainen log->files->hdr.prev_file_seq != 0) {
657afb33796f8216c568ad813627da89970760beTimo Sirainen /* we couldn't read dovecot.index and we don't have the first
657afb33796f8216c568ad813627da89970760beTimo Sirainen .log file, so just start from scratch */
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen mail_transaction_log_close(log);
226259ee6fb9830dafc1a5ba1e95bf5a4345b406Timo Sirainen }
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen i_free(log->filepath);
ab122a3bbae3b5fd2aad66e2f2840149d98cee52Timo Sirainen i_free(log->filepath2);
ab122a3bbae3b5fd2aad66e2f2840149d98cee52Timo Sirainen log->filepath = i_strconcat(log->index->filepath,
ab122a3bbae3b5fd2aad66e2f2840149d98cee52Timo Sirainen MAIL_TRANSACTION_LOG_SUFFIX, NULL);
ab122a3bbae3b5fd2aad66e2f2840149d98cee52Timo Sirainen log->filepath2 = i_strconcat(log->filepath, ".2", NULL);
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch
ab90f702ceedb7ba445a9a592be0b213b27cbafaStephan Bosch if (log->head != NULL)
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen mail_transaction_log_file_move_to_memory(log->head);
e76073ebaf90fa29abfdc364873acf78983949aaTimo Sirainen else {
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen file = mail_transaction_log_file_alloc_in_memory(log);
78ed6a99e980228a75fa59cff84327dc0ea82857Timo Sirainen mail_transaction_log_set_head(log, file);
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen }
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen}
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainenvoid mail_transaction_log_indexid_changed(struct mail_transaction_log *log)
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen{
a8e132559a7ebe54c8269d79ce29fa3338c76199Timo Sirainen struct mail_transaction_log_file *file;
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen mail_transaction_logs_clean(log);
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen
1e21e6be70994b1aa9e52ca0e2f51afefca6d0dfTimo Sirainen for (file = log->files; file != NULL; file = file->next) {
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen if (file->hdr.indexid != log->index->indexid) {
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen mail_transaction_log_file_set_corrupted(file,
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen "indexid changed: %u -> %u",
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen file->hdr.indexid, log->index->indexid);
8eb94c5190ba09bb6f6f068eec7bf96750f08d1dTimo Sirainen }
97c339398f1aba6f315b55a9b6ee6b020e33bea4Timo Sirainen }
e4d34f2fbee451219599d71505594df704093ce3Timo Sirainen
31e7be5e1d41a77f08d26cef46aba1df24b3f1baTimo Sirainen if (log->head != NULL &&
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen log->head->hdr.indexid != log->index->indexid) {
1294c06fcf841b62b0e40de3388354107c7fc012Timo Sirainen if (--log->head->refcount == 0)
1294c06fcf841b62b0e40de3388354107c7fc012Timo Sirainen mail_transaction_log_file_free(&log->head);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen (void)mail_transaction_log_create(log, FALSE);
553308791c097219e8eb31cbd03a29e9e1333848Timo Sirainen }
553308791c097219e8eb31cbd03a29e9e1333848Timo Sirainen}
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainenvoid mail_transaction_logs_clean(struct mail_transaction_log *log)
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen{
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen struct mail_transaction_log_file *file, *next;
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen /* remove only files from the beginning. this way if a view has
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen referenced an old file, it can still find the new files even if
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen there aren't any references to it currently. */
8be0af2dd41283fdd250b9a0d8fad28bfe80ca85Timo Sirainen for (file = log->files; file != NULL; file = next) {
8be0af2dd41283fdd250b9a0d8fad28bfe80ca85Timo Sirainen next = file->next;
8be0af2dd41283fdd250b9a0d8fad28bfe80ca85Timo Sirainen
8be0af2dd41283fdd250b9a0d8fad28bfe80ca85Timo Sirainen i_assert(file->refcount >= 0);
6fabfb7bbfd88d0c1de66981e52850f26067623bTimo Sirainen if (file->refcount > 0)
c6f894e1522f7b0b6068c228900914073c145175Timo Sirainen break;
c6f894e1522f7b0b6068c228900914073c145175Timo Sirainen
6420154390295de70cfd90be8766f2b10f07c1a8Timo Sirainen mail_transaction_log_file_free(&file);
6420154390295de70cfd90be8766f2b10f07c1a8Timo Sirainen }
6420154390295de70cfd90be8766f2b10f07c1a8Timo Sirainen /* sanity check: we shouldn't have locked refcount=0 files */
6420154390295de70cfd90be8766f2b10f07c1a8Timo Sirainen for (; file != NULL; file = file->next) {
6420154390295de70cfd90be8766f2b10f07c1a8Timo Sirainen i_assert(!file->locked || file->refcount > 0);
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen }
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen i_assert(log->head == NULL || log->files != NULL);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen}
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen
4bbc8a478be20d0be16e92179fc32327004ebf86Timo Sirainen#define LOG_WANT_ROTATE(file) \
4bbc8a478be20d0be16e92179fc32327004ebf86Timo Sirainen (((file)->sync_offset > MAIL_TRANSACTION_LOG_ROTATE_MIN_SIZE && \
4bbc8a478be20d0be16e92179fc32327004ebf86Timo Sirainen (time_t)(file)->hdr.create_stamp < \
fca7aa88ab19375aba5a8fd9d4100ea885dccb68Timo Sirainen ioloop_time - MAIL_TRANSACTION_LOG_ROTATE_TIME) || \
3c9783956dea385b322cd7fa6bf8c98c17a907a0Timo Sirainen ((file)->sync_offset > MAIL_TRANSACTION_LOG_ROTATE_MAX_SIZE))
3c9783956dea385b322cd7fa6bf8c98c17a907a0Timo Sirainen
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainenbool mail_transaction_log_want_rotate(struct mail_transaction_log *log)
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen{
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen return LOG_WANT_ROTATE(log->head);
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen}
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainenint mail_transaction_log_rotate(struct mail_transaction_log *log, bool reset)
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen{
3dadeec1ce7a5bf72fbd850658df1db3cedd4416Timo Sirainen struct mail_transaction_log_file *file;
3dadeec1ce7a5bf72fbd850658df1db3cedd4416Timo Sirainen const char *path = log->head->filepath;
8783c6a13c6107267d71bcb740fd096e96518883Timo Sirainen struct stat st;
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen int ret;
49e513d090753ccbf95560b2f3a21f081a5b6c51Timo Sirainen
43d32cbe60fdaef2699d99f1ca259053e9350411Timo Sirainen i_assert(log->head->locked);
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(log->index)) {
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen file = mail_transaction_log_file_alloc_in_memory(log);
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo Sirainen if (reset) {
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen file->hdr.prev_file_seq = 0;
b44650b0f48a4b5f0dc240ed836833a00b643b9fTimo Sirainen file->hdr.prev_file_offset = 0;
f968e62caa52a8924bd05ebf76ff515b5c18e17bTimo Sirainen }
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen } else {
a8e132559a7ebe54c8269d79ce29fa3338c76199Timo Sirainen /* we're locked, we shouldn't need to worry about ESTALE
35136dd2baf8dc30e4e754294ed81ff48e8c1e64Timo Sirainen problems in here. */
0f39a57760d93cddbce3ca43096d78e0fe2f42fdTimo Sirainen if (fstat(log->head->fd, &st) < 0) {
e9503210d3521a6833ed62dc332fc42ffb0e7a13Timo Sirainen mail_index_file_set_syscall_error(log->index,
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen log->head->filepath, "fstat()");
48ac75465ae154b1d705f18de6d95045ab714b65Timo Sirainen return -1;
48ac75465ae154b1d705f18de6d95045ab714b65Timo Sirainen }
e80203675151ef9d4f3f850cf02041042eb13096Timo Sirainen
file = mail_transaction_log_file_alloc(log, path);
file->st_dev = st.st_dev;
file->st_ino = st.st_ino;
file->last_mtime = st.st_mtime;
file->last_size = st.st_size;
if ((ret = mail_transaction_log_file_create(file, reset)) < 0) {
mail_transaction_log_file_free(&file);
return -1;
}
if (ret == 0) {
mail_index_set_error(log->index,
"Transaction log %s was recreated while we had it locked - "
"locking is broken (lock_method=%s)", path,
file_lock_method_to_str(log->index->lock_method));
mail_transaction_log_file_free(&file);
return -1;
}
i_assert(file->locked);
}
if (--log->head->refcount == 0)
mail_transaction_logs_clean(log);
else {
/* the newly created log file is already locked */
mail_transaction_log_file_unlock(log->head,
!log->index->log_sync_locked ? "rotating" :
"rotating while syncing");
}
mail_transaction_log_set_head(log, file);
return 0;
}
static int
mail_transaction_log_refresh(struct mail_transaction_log *log, bool nfs_flush)
{
struct mail_transaction_log_file *file;
struct stat st;
i_assert(log->head != NULL);
if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(log->head))
return 0;
if (nfs_flush && log->nfs_flush)
nfs_flush_file_handle_cache(log->filepath);
if (nfs_safe_stat(log->filepath, &st) < 0) {
if (errno != ENOENT) {
mail_index_file_set_syscall_error(log->index,
log->filepath,
"stat()");
return -1;
}
/* see if the whole directory got deleted */
if (nfs_safe_stat(log->index->dir, &st) < 0 &&
errno == ENOENT) {
log->index->index_deleted = TRUE;
return -1;
}
/* the file should always exist at this point. if it doesn't,
someone deleted it manually while the index was open. try to
handle this nicely by creating a new log file. */
file = log->head;
if (mail_transaction_log_create(log, FALSE) < 0)
return -1;
i_assert(file->refcount > 0);
file->refcount--;
log->index->need_recreate = TRUE;
return 0;
} else if (log->head->st_ino == st.st_ino &&
CMP_DEV_T(log->head->st_dev, st.st_dev)) {
/* NFS: log files get rotated to .log.2 files instead
of being unlinked, so we don't bother checking if
the existing file has already been unlinked here
(in which case inodes could match but point to
different files) */
return 0;
}
file = mail_transaction_log_file_alloc(log, log->filepath);
if (mail_transaction_log_file_open(file) <= 0) {
mail_transaction_log_file_free(&file);
return -1;
}
i_assert(!file->locked);
if (--log->head->refcount == 0)
mail_transaction_logs_clean(log);
mail_transaction_log_set_head(log, file);
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)
{
struct mail_transaction_log_file *file;
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. */
return 0;
}
if (mail_transaction_log_refresh(log, FALSE) < 0)
return -1;
if (file_seq > log->head->hdr.file_seq) {
if (!nfs_flush || !log->nfs_flush)
return 0;
/* try again, this time flush attribute cache */
if (mail_transaction_log_refresh(log, TRUE) < 0)
return -1;
if (file_seq > log->head->hdr.file_seq)
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))
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)) <= 0) {
mail_transaction_log_file_free(&file);
return ret;
}
/* but is it what we expected? */
if (file->hdr.file_seq != 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;
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);
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)
{
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) <= 0) {
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;
memset(set_r, 0, sizeof(*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;
}