mail-transaction-log.c revision fd71a4ab1c89b6a8cf7f3864a84868b2043fcc60
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainenmail_transaction_log_set_head(struct mail_transaction_log *log,
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen i_assert(log->files->next != NULL || log->files == file);
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainenmail_transaction_log_alloc(struct mail_index *index)
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainenstatic void mail_transaction_log_2_unlink_old(struct mail_transaction_log *log)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (ioloop_time - st.st_mtime >= (time_t)log->index->log_rotate_log2_stale_secs &&
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainenint mail_transaction_log_open(struct mail_transaction_log *log)
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen log->filepath = i_strconcat(log->index->filepath,
5ac0b0bf32898c63da086ae169674ecac151a31eTimo Sirainen log->filepath2 = i_strconcat(log->filepath, ".2", NULL);
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen /* these settings aren't available at alloc() time, so we need to
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen set them here: */
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen (log->index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0;
c28f6aa0b70af4811c9ace9114fe827c2f503455Timo Sirainen mail_transaction_log_file_free(&log->open_file);
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen file = mail_transaction_log_file_alloc(log, log->filepath);
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen if ((ret = mail_transaction_log_file_open(file, &reason)) <= 0) {
1a0ece3e873e3864269ed7eaed957dc10c56d25fTimo Sirainen /* leave the file for _create() */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenint mail_transaction_log_create(struct mail_transaction_log *log, bool reset)
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen file = mail_transaction_log_file_alloc_in_memory(log);
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen file = mail_transaction_log_file_alloc(log, log->filepath);
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen /* remember what file we tried to open. if someone else created
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen a new file, use it instead of recreating it */
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen file->last_mtime = log->open_file->last_mtime;
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainen mail_transaction_log_file_free(&log->open_file);
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (mail_transaction_log_file_create(file, reset) < 0) {
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainenvoid mail_transaction_log_close(struct mail_transaction_log *log)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen mail_transaction_log_file_free(&log->open_file);
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainenvoid mail_transaction_log_free(struct mail_transaction_log **_log)
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainenint mail_transaction_log_move_to_memory(struct mail_transaction_log *log)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen if (!log->index->initial_mapped && log->files != NULL &&
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen /* we couldn't read dovecot.index and we don't have the first
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen .log file, so just start from scratch */
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen log->filepath = i_strconcat(log->index->filepath,
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen log->filepath2 = i_strconcat(log->filepath, ".2", NULL);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen return mail_transaction_log_file_move_to_memory(log->head);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen file = mail_transaction_log_file_alloc_in_memory(log);
0a0cd45a633112a2ae6aad801c1e6afe53ab95deTimo Sirainenvoid mail_transaction_log_indexid_changed(struct mail_transaction_log *log)
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen for (file = log->files; file != NULL; file = file->next) {
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen if (file->hdr.indexid != log->index->indexid) {
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen "indexid changed: %u -> %u",
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen log->head->hdr.indexid != log->index->indexid) {
e4c81823af1fc43ca3f2ce9eb4af7fc8f57b13a5Timo Sirainen (void)mail_transaction_log_create(log, FALSE);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainenvoid mail_transaction_logs_clean(struct mail_transaction_log *log)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct mail_transaction_log_file *file, *next;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen /* remove only files from the beginning. this way if a view has
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen referenced an old file, it can still find the new files even if
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen there aren't any references to it currently. */
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen for (file = log->files; file != NULL; file = next) {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen /* sanity check: we shouldn't have locked refcount=0 files */
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen i_assert(!file->locked || file->refcount > 0);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen i_assert(log->head == NULL || log->files != NULL);
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainenbool mail_transaction_log_want_rotate(struct mail_transaction_log *log)
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen struct mail_transaction_log_file *file = log->head;
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if (file->hdr.major_version < MAIL_TRANSACTION_LOG_MAJOR_VERSION ||
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen (file->hdr.major_version == MAIL_TRANSACTION_LOG_MAJOR_VERSION &&
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen file->hdr.minor_version < MAIL_TRANSACTION_LOG_MINOR_VERSION)) {
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen /* upgrade immediately to a new log file format */
61e6367a259e2473f33df42fda8ceeb3b8b48416Timo Sirainen if (file->sync_offset > log->index->log_rotate_max_size) {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen /* file is too large, definitely rotate */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (file->sync_offset < log->index->log_rotate_min_size) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* file is still too small */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen /* rotate if the timestamp is old enough */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen ioloop_time - log->index->log_rotate_min_created_ago_secs;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenint mail_transaction_log_rotate(struct mail_transaction_log *log, bool reset)
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen file = mail_transaction_log_file_alloc_in_memory(log);
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen /* we're locked, we shouldn't need to worry about ESTALE
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen problems in here. */
e3fc1874694a8ddba9552ec23f9952f74f33d1d5Timo Sirainen file = mail_transaction_log_file_alloc(log, path);
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen if ((ret = mail_transaction_log_file_create(file, reset)) < 0) {
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen "Transaction log %s was recreated while we had it locked - "
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen file_lock_method_to_str(log->index->lock_method));
602a0434db30d8e3292d1c161a803d96a879a74fTimo Sirainen /* the newly created log file is already locked */
4334b9b032298defd4d3906f5357698ff016ead0Timo Sirainen "rotating while syncing");
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainenmail_transaction_log_refresh(struct mail_transaction_log *log, bool nfs_flush,
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(log->head)) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen *reason_r = t_strdup_printf("stat(%s) failed: %m", log->filepath);
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen /* see if the whole directory got deleted */
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen if (nfs_safe_stat(log->index->dir, &st) < 0 &&
4b9f99761df5014c659cd87fddaf6854af428cfcTimo Sirainen /* the file should always exist at this point. if it doesn't,
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen someone deleted it manually while the index was open. try to
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen handle this nicely by creating a new log file. */
7e1f68ad71d3485f1882142837b01f7a98ca8467Timo Sirainen if (mail_transaction_log_create(log, FALSE) < 0) {
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen /* NFS: log files get rotated to .log.2 files instead
a3c197999dfe2b0c8ea38cb77cfa5e95026005c0Timo Sirainen of being unlinked, so we don't bother checking if
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen the existing file has already been unlinked here
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen (in which case inodes could match but point to
923115fd382904fa13bb09bf307bf2835b52df60Timo Sirainen different files) */
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen file = mail_transaction_log_file_alloc(log, log->filepath);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (mail_transaction_log_file_open(file, reason_r) <= 0) {
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainenvoid mail_transaction_log_get_mailbox_sync_pos(struct mail_transaction_log *log,
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainenvoid mail_transaction_log_set_mailbox_sync_pos(struct mail_transaction_log *log,
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen i_assert(file_seq == log->head->hdr.file_seq);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen i_assert(file_offset >= log->head->saved_tail_offset);
e0c3d5460d1cc0c440cb7723c8c2eef8d0afe9b9Timo Sirainen if (file_offset >= log->head->max_tail_offset)
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainenint mail_transaction_log_find_file(struct mail_transaction_log *log,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen /* see if the .log file has been recreated */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* transaction log is locked. there's no way a newer
0611067f385a37773800225256dcd5cf6aa34212Timo Sirainen file exists. */
df16c7e87511fed827e6890a2a47d13ca48716deTimo Sirainen *reason_r = "Log is locked - newer log can't exist";
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (mail_transaction_log_refresh(log, FALSE, &reason) < 0) {
51e1a1c280ccb461a15827f7987d09cb9708b6e3Timo Sirainen "Requested newer log than exists: %s", reason);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* try again, this time flush attribute cache */
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen if (mail_transaction_log_refresh(log, TRUE, &reason) < 0) {
89e195dfb5c4b0efd9b9f459771a4467674e5b1fTimo Sirainen "Log refresh with NFS flush failed: %s", reason);
1036ad17ac837a451f6b045cac504d3efa2edb8eTimo Sirainen "Requested newer log than exists - "
a0b6b441fc679e562e79be0fb2819ffc24ab5b74Timo Sirainen for (file = log->files; file != NULL; file = file->next) {
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen /* see if we have it in log.2 file */
6f08b98ac63c25b747120d0c8f8e319b4e26ab0fTimo Sirainen file = mail_transaction_log_file_alloc(log, log->filepath2);
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen if ((ret = mail_transaction_log_file_open(file, reason_r)) <= 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* but is it what we expected? */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen *reason_r = t_strdup_printf(".log.2 contains file_seq=%u",
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainenint mail_transaction_log_lock_head(struct mail_transaction_log *log,
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen /* we want to get the head file locked. this is a bit racy,
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen since by the time we have it locked a new log file may have been
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen creating new log file requires locking the head file, so if we
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen can lock it and don't see another file, we can be sure no-one is
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen creating a new log at the moment */
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen ret = mail_transaction_log_refresh(log, TRUE, &reason);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen mail_transaction_log_file_unlock(file, t_strdup_printf(
37ab3cde96bfa4bc5304c0c348fc420aec79572dTimo Sirainen /* success */
f4bbeadda12fbd7c219063db68f3e78646d83c2cTimo Sirainen lock_secs = file->lock_created - lock_wait_started;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen mail_transaction_log_file_unlock(file, t_strdup_printf(
90b8f131849540fa374aede95edd86d47d35c09dTimo Sirainen /* try again */
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen if (lock_secs > MAIL_TRANSACTION_LOG_LOCK_WARN_SECS) {
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen i_warning("Locking transaction log file %s took %ld seconds (%s)",
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen log->head->filepath, (long)lock_secs, lock_reason);
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainenint mail_transaction_log_sync_lock(struct mail_transaction_log *log,
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen /* we need to check once in a while if .log.2 should be deleted
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen to avoid wasting space on such old files. but we also don't
904f9d5654b9c39edcdf32883e5e88771faf4d69Timo Sirainen want to waste time on checking it when the same mailbox
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen gets opened over and over again rapidly (e.g. pop3). so
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen do this only when there have actually been some changes
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen to mailbox (i.e. when it's being locked here) */
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen if (mail_transaction_log_lock_head(log, lock_reason) < 0)
7662010b03ffe5f2a6ecf4b4eb220d1c65efea76Timo Sirainen /* update sync_offset */
fe363b433b8038a69b55169da9dca27892ad7d18Timo Sirainen if (mail_transaction_log_file_map(log->head, log->head->sync_offset,
97e62b2b36dda0acb3215667042f5c80cdee8155Timo Sirainen "Failed to map transaction log %s at "
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen "sync_offset=%"PRIuUOFF_T" after locking: %s",
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen log->head->filepath, log->head->sync_offset, reason);
7d5d50dd9a8c2539d7025a69e39d34fca56daeafTimo Sirainen mail_transaction_log_file_unlock(log->head, t_strdup_printf(
14175321ddb88619015866978c05a27786ca4814Timo Sirainenvoid mail_transaction_log_sync_unlock(struct mail_transaction_log *log,
14175321ddb88619015866978c05a27786ca4814Timo Sirainen mail_transaction_log_file_unlock(log->head, lock_reason);
14175321ddb88619015866978c05a27786ca4814Timo Sirainenvoid mail_transaction_log_get_head(struct mail_transaction_log *log,
439dd06aec3301e65d650f6dc1d4a1a00b356b4fTimo Sirainenvoid mail_transaction_log_get_tail(struct mail_transaction_log *log,
14175321ddb88619015866978c05a27786ca4814Timo Sirainen struct mail_transaction_log_file *tail, *file = log->files;
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen for (tail = file; file->next != NULL; file = file->next) {
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen if (file->hdr.file_seq + 1 != file->next->hdr.file_seq)
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainenbool mail_transaction_log_is_head_prev(struct mail_transaction_log *log,
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen return log->head->hdr.prev_file_seq == file_seq &&
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainen log->head->hdr.prev_file_offset == file_offset;
c7acd38cd4ef76a0f4652f9ca659ea5e64458b52Timo Sirainenint mail_transaction_log_get_mtime(struct mail_transaction_log *log,
282a436a74d8835edb45cc019b1c916013013fd3Timo Sirainen mail_index_file_set_syscall_error(log->index, log->filepath,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenint mail_transaction_log_unlink(struct mail_transaction_log *log)
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen mail_index_file_set_syscall_error(log->index, log->filepath,
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainenvoid mail_transaction_log_get_dotlock_set(struct mail_transaction_log *log,
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen set_r->timeout = I_MIN(MAIL_TRANSACTION_LOG_LOCK_TIMEOUT,
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen set_r->stale_timeout = MAIL_TRANSACTION_LOG_LOCK_CHANGE_TIMEOUT;
01e606cda5192c4254c090624a0b2ca92da6da8eTimo Sirainen set_r->nfs_flush = (index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0;