mail-transaction-log.c revision 2454dfa32c93c20a8522c6ed42fe057baaac9f9a
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "lib.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "ioloop.h"
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#include "buffer.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "file-dotlock.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "nfs-workarounds.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "mmap-util.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "mail-index-private.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include "mail-transaction-log-private.h"
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen#include <stddef.h>
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen#include <stdio.h>
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen#include <sys/stat.h>
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainenstatic void
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainenmail_transaction_log_set_head(struct mail_transaction_log *log,
5be5d875996999585de785ac33f96ff1569f1a0eTimo Sirainen struct mail_transaction_log_file *file)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen{
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen i_assert(log->head != file);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen file->refcount++;
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen log->head = file;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen i_assert(log->files != NULL);
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen i_assert(log->files->next != NULL || log->files == file);
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen}
b87436ebb957a9eb182be72ba00e2c8eae59a2e4Timo Sirainen
fc84f8af4794f4bb6caf6e5ec3fb1f8cebd0462aTimo Sirainenstruct mail_transaction_log *
b8a4aab1f117f6760184ad50b1af41ba810b51f9Timo Sirainenmail_transaction_log_alloc(struct mail_index *index)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen{
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen struct mail_transaction_log *log;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen
81e4bda7d481c57cd049a0a68daab733b1ca9c44Timo Sirainen log = i_new(struct mail_transaction_log, 1);
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen log->index = index;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen return log;
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen}
d7babe048f9ae1afa5357973b8de8c929753a216Timo Sirainen
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainenstatic void mail_transaction_log_2_unlink_old(struct mail_transaction_log *log)
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen{
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen struct stat st;
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(log->index))
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen return;
3851ad9fcb25635f02b46d44586742ef1081876bTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (stat(log->filepath2, &st) < 0) {
ad4f1b0666975c57dd2d8d3492b223ec814791cdTimo Sirainen if (errno != ENOENT && errno != ESTALE) {
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen mail_index_set_error(log->index,
503e5ef896c7b4a51cf73efb0d132860a8c747e6Timo Sirainen "stat(%s) failed: %m", log->filepath2);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen }
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return;
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen }
9b61a6db87c026656f8d2ae214e4486b98a069c0Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen if (ioloop_time - st.st_mtime >= (time_t)log->index->log_rotate_log2_stale_secs &&
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen !log->index->readonly)
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen i_unlink_if_exists(log->filepath2);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen}
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen
cb931f84e3ec8e3deda253a1c0ae0409023de096Timo Sirainenint mail_transaction_log_open(struct mail_transaction_log *log)
12dc81583d1958cb301a617e19fbd40e8d376397Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_transaction_log_file *file;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen const char *reason;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen int ret;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen i_free(log->filepath);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen i_free(log->filepath2);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen log->filepath = i_strconcat(log->index->filepath,
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen MAIL_TRANSACTION_LOG_SUFFIX, NULL);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen log->filepath2 = i_strconcat(log->filepath, ".2", NULL);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen
6d6bbe8787354bbb69d0c03187adfe0f497d70b8Timo Sirainen /* these settings aren't available at alloc() time, so we need to
048e40f9364fa68482bc276dd4a5d595a3d742e9Timo Sirainen set them here: */
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen log->nfs_flush =
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen (log->index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0;
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
3fe44a0df5a0bdd80c495f79cbf0e384441d6fccTimo Sirainen if (log->open_file != NULL)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen mail_transaction_log_file_free(&log->open_file);
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(log->index))
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen return 0;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen file = mail_transaction_log_file_alloc(log, log->filepath);
73583cff4f0ca9ee87204256ca1994adf17cb94cTimo Sirainen if ((ret = mail_transaction_log_file_open(file, &reason)) <= 0) {
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen /* leave the file for _create() */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen log->open_file = file;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen return ret;
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen mail_transaction_log_set_head(log, file);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen return 1;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen}
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainenint mail_transaction_log_create(struct mail_transaction_log *log, bool reset)
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen{
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen struct mail_transaction_log_file *file;
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(log->index)) {
ad0fe438255666726723a93f3112df6e103028afTimo Sirainen file = mail_transaction_log_file_alloc_in_memory(log);
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen mail_transaction_log_set_head(log, file);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen return 0;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen file = mail_transaction_log_file_alloc(log, log->filepath);
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen if (log->open_file != NULL) {
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen /* remember what file we tried to open. if someone else created
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen a new file, use it instead of recreating it */
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen file->st_ino = log->open_file->st_ino;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen file->st_dev = log->open_file->st_dev;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen file->last_size = log->open_file->last_size;
2521fd0986302cdabc8b0711eef63ac188f32cd6Timo Sirainen file->last_mtime = log->open_file->last_mtime;
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen mail_transaction_log_file_free(&log->open_file);
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen }
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen if (mail_transaction_log_file_create(file, reset) < 0) {
766115d2b2e6dbcf59f90d3b3866851cf6f740feTimo Sirainen mail_transaction_log_file_free(&file);
766115d2b2e6dbcf59f90d3b3866851cf6f740feTimo Sirainen return -1;
766115d2b2e6dbcf59f90d3b3866851cf6f740feTimo Sirainen }
766115d2b2e6dbcf59f90d3b3866851cf6f740feTimo Sirainen
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen mail_transaction_log_set_head(log, file);
2d8b23805db6f06b8b38174fb6e135386694f429Timo Sirainen return 1;
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen}
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainenvoid mail_transaction_log_close(struct mail_transaction_log *log)
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen{
0e5819a061034f1636b124c03a89f67d37c852b1Timo Sirainen i_assert(log->views == NULL);
0e5819a061034f1636b124c03a89f67d37c852b1Timo Sirainen
0e5819a061034f1636b124c03a89f67d37c852b1Timo Sirainen if (log->open_file != NULL)
b1678954f83e1059b981e2def52a70054fa71399Timo Sirainen mail_transaction_log_file_free(&log->open_file);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen if (log->head != NULL)
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen log->head->refcount--;
d5d23d5ff8b7a06d2ead489ddcf55ee8fb5ca7b6Timo Sirainen mail_transaction_logs_clean(log);
d5d23d5ff8b7a06d2ead489ddcf55ee8fb5ca7b6Timo Sirainen i_assert(log->files == NULL);
d5d23d5ff8b7a06d2ead489ddcf55ee8fb5ca7b6Timo Sirainen}
d19d3aa4eafa34b48b7d7d311c9db31e1898576aTimo Sirainen
d19d3aa4eafa34b48b7d7d311c9db31e1898576aTimo Sirainenvoid mail_transaction_log_free(struct mail_transaction_log **_log)
8eb223b84389a7b75a39d46484f5166d221305ebTimo Sirainen{
8eb223b84389a7b75a39d46484f5166d221305ebTimo Sirainen struct mail_transaction_log *log = *_log;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen *_log = NULL;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen mail_transaction_log_close(log);
8e371a3ce32bd64288786855b8ce0cb63f19f7d1Timo Sirainen log->index->log = NULL;
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_free(log->filepath);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_free(log->filepath2);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen i_free(log);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen}
30ad2b0309119501efad06c72ec9b1561b90d4afTimo Sirainen
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainenvoid mail_transaction_log_move_to_memory(struct mail_transaction_log *log)
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen{
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen struct mail_transaction_log_file *file;
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen if (!log->index->initial_mapped && log->files != NULL &&
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen log->files->hdr.prev_file_seq != 0) {
bc7a4cf2c06702ebaedba9a7c15ce657d5856f63Timo Sirainen /* we couldn't read dovecot.index and we don't have the first
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen .log file, so just start from scratch */
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen mail_transaction_log_close(log);
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen }
269370f66e320a071700e33d9f2584eb46d96a90Timo Sirainen
fbdd091ef000e3ec4db34c054736c91ef9bc48ceTimo Sirainen i_free(log->filepath);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen i_free(log->filepath2);
6800c6607013d4fdef5a4f764bae407301c6cce8Timo Sirainen log->filepath = i_strconcat(log->index->filepath,
b2e181e33889f0a4a3ba9dc23d676cbfe1bf7782Timo Sirainen MAIL_TRANSACTION_LOG_SUFFIX, NULL);
3ccfcf0856958cb9208a9fc51c3bdf13c58ad52aTimo Sirainen log->filepath2 = i_strconcat(log->filepath, ".2", NULL);
bd7b1a9000b12349e2a99bb43b3ce8b96a18e92bTimo Sirainen
aaebcf0da12df7216be69961204fa64ec24c54b9Timo Sirainen if (log->head != NULL)
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen mail_transaction_log_file_move_to_memory(log->head);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen else {
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainen file = mail_transaction_log_file_alloc_in_memory(log);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen mail_transaction_log_set_head(log, file);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen}
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen
82995cc154a929f37aa486a72a6485e9f8d34a30Timo Sirainenvoid mail_transaction_log_indexid_changed(struct mail_transaction_log *log)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen struct mail_transaction_log_file *file;
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen
d23c747de9d33966483fbdd41f08ad7766da7c5cTimo Sirainen mail_transaction_logs_clean(log);
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen
d761c26f2bbf514e0fc0c6ed9bc52627a4179eabTimo Sirainen for (file = log->files; file != NULL; file = file->next) {
d761c26f2bbf514e0fc0c6ed9bc52627a4179eabTimo Sirainen if (file->hdr.indexid != log->index->indexid) {
d761c26f2bbf514e0fc0c6ed9bc52627a4179eabTimo Sirainen mail_transaction_log_file_set_corrupted(file,
2b682d8d3661800f16aceaa45fa4de9b6b140a59Timo Sirainen "indexid changed: %u -> %u",
62300a38f91227b9de043a9a8ec1d4f1978e1138Timo Sirainen file->hdr.indexid, log->index->indexid);
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen }
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen }
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (log->head != NULL &&
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen log->head->hdr.indexid != log->index->indexid) {
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen if (--log->head->refcount == 0)
b863b7e3fccf75f90f613b875f02fd1781e14e5eTimo Sirainen mail_transaction_log_file_free(&log->head);
1795e934ebcd58175d3b5bbdd811b13c7889efa3Timo Sirainen (void)mail_transaction_log_create(log, FALSE);
}
}
void mail_transaction_logs_clean(struct mail_transaction_log *log)
{
struct mail_transaction_log_file *file, *next;
/* remove only files from the beginning. this way if a view has
referenced an old file, it can still find the new files even if
there aren't any references to it currently. */
for (file = log->files; file != NULL; file = next) {
next = file->next;
i_assert(file->refcount >= 0);
if (file->refcount > 0)
break;
mail_transaction_log_file_free(&file);
}
/* sanity check: we shouldn't have locked refcount=0 files */
for (; file != NULL; file = file->next) {
i_assert(!file->locked || file->refcount > 0);
}
i_assert(log->head == NULL || log->files != NULL);
}
#define LOG_WANT_ROTATE(file) \
(((file)->sync_offset > (file)->log->index->log_rotate_min_size && \
(file)->hdr.create_stamp < \
ioloop_time - (file)->log->index->log_rotate_min_created_ago_secs) || \
((file)->sync_offset > (file)->log->index->log_rotate_max_size))
bool mail_transaction_log_want_rotate(struct mail_transaction_log *log)
{
return LOG_WANT_ROTATE(log->head);
}
int mail_transaction_log_rotate(struct mail_transaction_log *log, bool reset)
{
struct mail_transaction_log_file *file;
const char *path = log->head->filepath;
struct stat st;
int ret;
i_assert(log->head->locked);
if (MAIL_INDEX_IS_IN_MEMORY(log->index)) {
file = mail_transaction_log_file_alloc_in_memory(log);
if (reset) {
file->hdr.prev_file_seq = 0;
file->hdr.prev_file_offset = 0;
}
} else {
/* we're locked, we shouldn't need to worry about ESTALE
problems in here. */
if (fstat(log->head->fd, &st) < 0) {
mail_index_file_set_syscall_error(log->index,
log->head->filepath, "fstat()");
return -1;
}
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,
const char **reason_r)
{
struct mail_transaction_log_file *file;
struct stat st;
i_assert(log->head != NULL);
if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(log->head)) {
*reason_r = "Log is in memory";
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()");
*reason_r = t_strdup_printf("stat(%s) failed: %m", log->filepath);
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;
*reason_r = "Index directory was deleted";
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) {
*reason_r = "Failed to create log";
return -1;
}
i_assert(file->refcount > 0);
file->refcount--;
log->index->need_recreate = TRUE;
*reason_r = "Log created";
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) */
*reason_r = "Log inode is unchanged";
return 0;
}
file = mail_transaction_log_file_alloc(log, log->filepath);
if (mail_transaction_log_file_open(file, reason_r) <= 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);
*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)
{
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;
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;
}