mail-transaction-log.c revision 6c2ddb9f586e6392552ddfb82ab55e57fcfc4110
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen/* Copyright (c) 2003-2010 Dovecot authors, see the included COPYING file */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "lib.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "ioloop.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "buffer.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "file-dotlock.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "nfs-workarounds.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "close-keep-errno.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "mmap-util.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "mail-index-private.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "mail-transaction-log-private.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include <stddef.h>
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include <stdio.h>
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen#include <sys/stat.h>
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#define LOG_NEW_DOTLOCK_SUFFIX ".newlock"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_transaction_log_set_head(struct mail_transaction_log *log,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mail_transaction_log_file *file)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_assert(log->head != file);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen file->refcount++;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen log->head = file;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_assert(log->files != NULL);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_assert(log->files->next != NULL || log->files == file);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainenstruct mail_transaction_log *
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_transaction_log_alloc(struct mail_index *index)
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen{
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen struct mail_transaction_log *log;
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen log = i_new(struct mail_transaction_log, 1);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen log->index = index;
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen log->dotlock_settings.timeout = MAIL_TRANSCATION_LOG_LOCK_TIMEOUT;
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen log->dotlock_settings.stale_timeout =
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen MAIL_TRANSCATION_LOG_LOCK_CHANGE_TIMEOUT;
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen log->new_dotlock_settings = log->dotlock_settings;
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen log->new_dotlock_settings.lock_suffix = LOG_NEW_DOTLOCK_SUFFIX;
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen return log;
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen}
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainenstatic void mail_transaction_log_2_unlink_old(struct mail_transaction_log *log)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen struct stat st;
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen if (stat(log->filepath2, &st) < 0) {
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen if (errno != ENOENT && errno != ESTALE) {
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen mail_index_set_error(log->index,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen "stat(%s) failed: %m", log->filepath2);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen if (st.st_mtime + MAIL_TRANSACTION_LOG2_STALE_SECS <= ioloop_time &&
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen !log->index->readonly) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (unlink(log->filepath2) < 0 && errno != ENOENT) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_index_set_error(log->index,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen "unlink(%s) failed: %m", log->filepath2);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenint mail_transaction_log_open(struct mail_transaction_log *log)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mail_transaction_log_file *file;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen int ret;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_free(log->filepath);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen i_free(log->filepath2);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen log->filepath = i_strconcat(log->index->filepath,
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen MAIL_TRANSACTION_LOG_SUFFIX, NULL);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen log->filepath2 = i_strconcat(log->filepath, ".2", NULL);
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen
6620e0259ec2cb2e45a04724079d1f0fd5a0b258Timo Sirainen log->flags = log->index->flags;
6620e0259ec2cb2e45a04724079d1f0fd5a0b258Timo Sirainen log->dotlock_settings.use_excl_lock =
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen (log->flags & MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL) != 0;
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen log->dotlock_settings.nfs_flush =
ba90e657bc68a72ab3b3021e2f4a874fac9965baTimo Sirainen (log->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0;
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen log->new_dotlock_settings.use_excl_lock =
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen (log->flags & MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL) != 0;
c02a056b724abd6578fb8c4e439de0e94eaea6feTimo Sirainen log->new_dotlock_settings.nfs_flush =
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen (log->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (log->open_file != NULL)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_transaction_log_file_free(&log->open_file);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(log->index))
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen file = mail_transaction_log_file_alloc(log, log->filepath);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if ((ret = mail_transaction_log_file_open(file, FALSE)) <= 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* leave the file for _create() */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen log->open_file = file;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return ret;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_transaction_log_set_head(log, file);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_transaction_log_2_unlink_old(log);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen return 1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
9c93e2e3e35bff1bddbe59cef7be296aa24bd79dTimo Sirainenint mail_transaction_log_create(struct mail_transaction_log *log, bool reset)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mail_transaction_log_file *file;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(log->index)) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen file = mail_transaction_log_file_alloc_in_memory(log);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_transaction_log_set_head(log, file);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return 0;
9c93e2e3e35bff1bddbe59cef7be296aa24bd79dTimo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen file = mail_transaction_log_file_alloc(log, log->filepath);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (log->open_file != NULL) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* remember what file we tried to open. if someone else created
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen a new file, use it instead of recreating it */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen file->st_ino = log->open_file->st_ino;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen file->st_dev = log->open_file->st_dev;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen file->last_size = log->open_file->last_size;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen file->last_mtime = log->open_file->last_mtime;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_transaction_log_file_free(&log->open_file);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (mail_transaction_log_file_create(file, reset) < 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_transaction_log_file_free(&file);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
9c93e2e3e35bff1bddbe59cef7be296aa24bd79dTimo Sirainen mail_transaction_log_set_head(log, file);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return 1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenvoid mail_transaction_log_close(struct mail_transaction_log *log)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_transaction_log_views_close(log);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (log->open_file != NULL)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_transaction_log_file_free(&log->open_file);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (log->head != NULL)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen log->head->refcount--;
a7bee3930831a9261fa6180d02af29c484d862e9Timo Sirainen mail_transaction_logs_clean(log);
a7bee3930831a9261fa6180d02af29c484d862e9Timo Sirainen i_assert(log->files == NULL);
a7bee3930831a9261fa6180d02af29c484d862e9Timo Sirainen}
a7bee3930831a9261fa6180d02af29c484d862e9Timo Sirainen
a7bee3930831a9261fa6180d02af29c484d862e9Timo Sirainenvoid mail_transaction_log_free(struct mail_transaction_log **_log)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mail_transaction_log *log = *_log;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen *_log = NULL;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_transaction_log_close(log);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen log->index->log = NULL;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_free(log->filepath);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen i_free(log->filepath2);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen i_free(log);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainenvoid mail_transaction_log_move_to_memory(struct mail_transaction_log *log)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen struct mail_transaction_log_file *file;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (!log->index->initial_mapped && log->files != NULL &&
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen log->files->hdr.prev_file_seq != 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* we couldn't read dovecot.index and we don't have the first
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen .log file, so just start from scratch */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_transaction_log_close(log);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_free(log->filepath);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_free(log->filepath2);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen log->filepath = i_strconcat(log->index->filepath,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen MAIL_TRANSACTION_LOG_SUFFIX, NULL);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen log->filepath2 = i_strconcat(log->filepath, ".2", NULL);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (log->head != NULL)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_transaction_log_file_move_to_memory(log->head);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen else {
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen file = mail_transaction_log_file_alloc_in_memory(log);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen mail_transaction_log_set_head(log, file);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen }
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen}
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainenvoid mail_transaction_log_indexid_changed(struct mail_transaction_log *log)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen{
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen struct mail_transaction_log_file *file;
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen mail_transaction_logs_clean(log);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for (file = log->files; file != NULL; file = file->next) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (file->hdr.indexid != log->index->indexid) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_transaction_log_file_set_corrupted(file,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen "indexid changed: %u -> %u",
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen file->hdr.indexid, log->index->indexid);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (log->head != NULL &&
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen log->head->hdr.indexid != log->index->indexid) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (--log->head->refcount == 0)
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen mail_transaction_log_file_free(&log->head);
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen (void)mail_transaction_log_create(log, FALSE);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenvoid mail_transaction_logs_clean(struct mail_transaction_log *log)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mail_transaction_log_file *file, *next;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* remove only files from the beginning. this way if a view has
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen referenced an old file, it can still find the new files even if
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen there aren't any references to it currently. */
dc9bfb7dc057964238e181d3d8b08751527bb08aTimo Sirainen for (file = log->files; file != NULL; file = next) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen next = file->next;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_assert(file->refcount >= 0);
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen if (file->refcount > 0)
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen break;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_transaction_log_file_free(&file);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* if we still have locked files with refcount=0, unlock them */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for (; file != NULL; file = file->next) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (file->locked && file->refcount == 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_transaction_log_file_unlock(file);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_assert(log->head == NULL || log->files != NULL);
}
#define LOG_WANT_ROTATE(file) \
(((file)->sync_offset > MAIL_TRANSACTION_LOG_ROTATE_MIN_SIZE && \
(time_t)(file)->hdr.create_stamp < \
ioloop_time - MAIL_TRANSACTION_LOG_ROTATE_TIME) || \
((file)->sync_offset > MAIL_TRANSACTION_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;
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,
file->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 (mail_transaction_log_file_create(file, reset) < 0) {
mail_transaction_log_file_free(&file);
return -1;
}
}
if (--log->head->refcount == 0)
mail_transaction_logs_clean(log);
else
mail_transaction_log_file_unlock(log->head);
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->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0)
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;
}
/* 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, FALSE) <= 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->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) == 0)
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, TRUE)) <= 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)
{
struct mail_transaction_log_file *file;
int ret = 0;
/* 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 */
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_logs_clean(log);
file = NULL;
}
if (ret == 0 && log->head == file) {
/* success */
break;
}
if (file != NULL)
mail_transaction_log_file_unlock(file);
if (ret < 0)
break;
/* try again */
}
return ret;
}
int mail_transaction_log_sync_lock(struct mail_transaction_log *log,
uint32_t *file_seq_r, uoff_t *file_offset_r)
{
i_assert(!log->index->log_locked);
if (mail_transaction_log_lock_head(log) < 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);
return -1;
}
log->index->log_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)
{
i_assert(log->index->log_locked);
log->index->log_locked = FALSE;
mail_transaction_log_file_unlock(log->head);
}
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;
}