mail-transaction-log.c revision 2bc03d667b58c0a8765a66c52c5718cf62a79cfb
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "lib.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "ioloop.h"
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen#include "buffer.h"
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#include "file-dotlock.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "nfs-workarounds.h"
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen#include "close-keep-errno.h"
994bb1a8a80da664083691d41dd9aec5d6fba2bfTimo Sirainen#include "read-full.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "write-full.h"
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen#include "mmap-util.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "mail-index-private.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "mail-index-view-private.h"
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#include "mail-transaction-log-private.h"
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#include "mail-transaction-util.h"
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include "mail-index-transaction-private.h"
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen#include <stddef.h>
b66412da78711db8423288847ecfb08469609a03Timo Sirainen#include <stdio.h>
b66412da78711db8423288847ecfb08469609a03Timo Sirainen#include <sys/stat.h>
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#define LOG_PREFETCH 1024
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen/* this lock should never exist for a long time.. */
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#define LOG_DOTLOCK_TIMEOUT 60
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen#define LOG_DOTLOCK_STALE_TIMEOUT 60
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen#define MAIL_TRANSACTION_LOG_SUFFIX ".log"
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen#define LOG_NEW_DOTLOCK_SUFFIX ".newlock"
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenstatic struct mail_transaction_log_file *
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenmail_transaction_log_file_open_or_create(struct mail_transaction_log *log,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen const char *path);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenstatic struct mail_transaction_log_file *
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenmail_transaction_log_file_alloc_in_memory(struct mail_transaction_log *log);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenstatic int
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenmail_transaction_log_file_create(struct mail_transaction_log_file *file,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen bool lock, dev_t dev, ino_t ino,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen uoff_t file_size);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenstatic int
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainenmail_transaction_log_file_fd_open_or_create(struct mail_transaction_log_file
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen *file, bool try_retry);
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainenstatic int
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainenmail_transaction_log_file_read(struct mail_transaction_log_file *file,
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen uoff_t offset);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainenvoid
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainenmail_transaction_log_file_set_corrupted(struct mail_transaction_log_file *file,
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen const char *fmt, ...)
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen{
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen va_list va;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen file->hdr.indexid = 0;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (!MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (pwrite_full(file->fd, &file->hdr.indexid,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen sizeof(file->hdr.indexid), 0) < 0) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen file->filepath, "pwrite()");
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen }
39afc7584d935b2dc7332c21966a7b20da03f1ecTimo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen va_start(va, fmt);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen t_push();
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen mail_index_set_error(file->log->index,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen "Corrupted transaction log file %s: %s",
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen file->filepath, t_strdup_vprintf(fmt, va));
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen t_pop();
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen va_end(va);
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen if (file->log->index->log != NULL && file->log->index->map != NULL) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen /* this may have happened because of broken index.
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen make sure it's ok. */
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen (void)mail_index_fsck(file->log->index);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen}
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
b66412da78711db8423288847ecfb08469609a03Timo Sirainenstatic struct mail_transaction_log_file *
b66412da78711db8423288847ecfb08469609a03Timo Sirainenmail_transaction_log_file_alloc(struct mail_transaction_log *log,
b66412da78711db8423288847ecfb08469609a03Timo Sirainen const char *path)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen struct mail_transaction_log_file *file;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen file = i_new(struct mail_transaction_log_file, 1);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file->log = log;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file->filepath = i_strdup(path);
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen file->fd = -1;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen return file;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenstatic void
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenmail_transaction_log_file_free(struct mail_transaction_log_file *file)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen struct mail_transaction_log_file **p;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen int old_errno = errno;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen mail_transaction_log_file_unlock(file);
b66412da78711db8423288847ecfb08469609a03Timo Sirainen
b66412da78711db8423288847ecfb08469609a03Timo Sirainen for (p = &file->log->files; *p != NULL; p = &(*p)->next) {
b039dabf4c53f72454e795930e7643b6e0e625f9Timo Sirainen if (*p == file) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen *p = file->next;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen break;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (file == file->log->head)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen file->log->head = NULL;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (file->buffer != NULL)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen buffer_free(file->buffer);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (file->mmap_base != NULL) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (munmap(file->mmap_base, file->mmap_size) < 0) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen file->filepath,
b66412da78711db8423288847ecfb08469609a03Timo Sirainen "munmap()");
b66412da78711db8423288847ecfb08469609a03Timo Sirainen }
b66412da78711db8423288847ecfb08469609a03Timo Sirainen }
b66412da78711db8423288847ecfb08469609a03Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (file->fd != -1) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (close(file->fd) < 0) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen file->filepath,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen "close()");
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen i_free(file->filepath);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_free(file);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen errno = old_errno;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenstatic int
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenmail_transaction_log_file_dotlock(struct mail_transaction_log_file *file)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen int ret;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (file->log->dotlock_count > 0)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen ret = 1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen else {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen ret = file_dotlock_create(&file->log->dotlock_settings,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file->filepath, 0,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen &file->log->dotlock);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (ret > 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file->log->dotlock_count++;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file->locked = TRUE;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return 0;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (ret < 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen file->filepath,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen "file_dotlock_create()");
b66412da78711db8423288847ecfb08469609a03Timo Sirainen return -1;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
b66412da78711db8423288847ecfb08469609a03Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_index_set_error(file->log->index,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen "Timeout while waiting for release of "
b66412da78711db8423288847ecfb08469609a03Timo Sirainen "dotlock for transaction log file %s",
b66412da78711db8423288847ecfb08469609a03Timo Sirainen file->filepath);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen file->log->index->index_lock_timeout = TRUE;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen return -1;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenstatic int
b66412da78711db8423288847ecfb08469609a03Timo Sirainenmail_transaction_log_file_undotlock(struct mail_transaction_log_file *file)
b66412da78711db8423288847ecfb08469609a03Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen int ret;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (--file->log->dotlock_count > 0)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return 0;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen ret = file_dotlock_delete(&file->log->dotlock);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (ret < 0) {
5bd2cf0f30371cb0374b026322a6f52fdb20755fTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file->filepath, "file_dotlock_delete()");
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return -1;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (ret == 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_index_set_error(file->log->index,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen "Dotlock was lost for transaction log file %s",
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen file->filepath);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return -1;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return 0;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenstatic int
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainenmail_transaction_log_file_lock(struct mail_transaction_log_file *file)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen int ret;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if (file->locked)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return 0;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file->locked = TRUE;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return 0;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen if (file->log->index->lock_method == FILE_LOCK_METHOD_DOTLOCK)
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return mail_transaction_log_file_dotlock(file);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen i_assert(file->file_lock == NULL);
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen ret = mail_index_lock_fd(file->log->index, file->filepath, file->fd,
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen F_WRLCK, MAIL_INDEX_LOCK_SECS,
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen &file->file_lock);
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen if (ret > 0) {
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen file->locked = TRUE;
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen return 0;
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen }
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen if (ret < 0) {
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen file->filepath,
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen "mail_index_wait_lock_fd()");
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen return -1;
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen }
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen mail_index_set_error(file->log->index,
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen "Timeout while waiting for lock for transaction log file %s",
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen file->filepath);
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen file->log->index->index_lock_timeout = TRUE;
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen return -1;
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen}
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainenvoid mail_transaction_log_file_unlock(struct mail_transaction_log_file *file)
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen{
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen if (!file->locked)
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen return;
6b3f3a6561447afb13b2fcc22231d13653e8aff6Timo Sirainen
6b3f3a6561447afb13b2fcc22231d13653e8aff6Timo Sirainen file->locked = FALSE;
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file))
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (file->log->index->lock_method == FILE_LOCK_METHOD_DOTLOCK) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_transaction_log_file_undotlock(file);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen }
b66412da78711db8423288847ecfb08469609a03Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file_unlock(&file->file_lock);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen#define INDEX_HAS_MISSING_LOGS(index, file) \
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen !(((file)->hdr.file_seq == (index)->hdr->log_file_seq && \
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen (index)->hdr->log_file_int_offset >= (file)->hdr.hdr_size) || \
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen ((file)->hdr.prev_file_seq == (index)->hdr->log_file_seq && \
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen (file)->hdr.prev_file_offset == (index)->hdr->log_file_int_offset))
9c0716dfcd6b575419776848dd7157186ef58d57Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenstatic int mail_transaction_log_check_file_seq(struct mail_transaction_log *log)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen struct mail_index *index = log->index;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen struct mail_transaction_log_file *file;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen unsigned int lock_id;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen int ret;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
b66412da78711db8423288847ecfb08469609a03Timo Sirainen if (mail_transaction_log_lock_head(log) < 0)
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen return -1;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file = log->head;
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen file->refcount++;
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen ret = mail_index_lock_shared(index, TRUE, &lock_id);
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen if (ret == 0) {
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen ret = mail_index_map(index, FALSE);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (ret <= 0)
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen ret = -1;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen else if (INDEX_HAS_MISSING_LOGS(index, file)) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* broken - fix it by creating a new log file */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen ret = mail_transaction_log_rotate(log, FALSE);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b66412da78711db8423288847ecfb08469609a03Timo Sirainen }
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (--file->refcount == 0)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_transaction_logs_clean(log);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen else
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_transaction_log_file_unlock(file);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return ret;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen}
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenstatic struct mail_transaction_log *
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenmail_transaction_log_open_int(struct mail_index *index, bool create)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen struct mail_transaction_log *log;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen struct mail_transaction_log_file *file;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen const char *path;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen log = i_new(struct mail_transaction_log, 1);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen log->index = index;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen log->dotlock_settings.use_excl_lock = index->use_excl_dotlocks;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen log->dotlock_settings.timeout = LOG_DOTLOCK_TIMEOUT;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen log->dotlock_settings.stale_timeout = LOG_DOTLOCK_STALE_TIMEOUT;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen log->new_dotlock_settings = log->dotlock_settings;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen log->new_dotlock_settings.lock_suffix = LOG_NEW_DOTLOCK_SUFFIX;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen path = t_strconcat(index->filepath,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen MAIL_TRANSACTION_LOG_SUFFIX, NULL);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(index))
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file = mail_transaction_log_file_alloc_in_memory(log);
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen else if (create) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen struct stat st;
afb7901ecb5d5566d4cf19be969654946fbaad4bTimo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen file = mail_transaction_log_file_alloc(log, path);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if (stat(path, &st) < 0)
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen memset(&st, 0, sizeof(st));
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen if (mail_transaction_log_file_create(file, FALSE,
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen st.st_dev, st.st_ino,
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen st.st_size) < 0 ||
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen mail_transaction_log_file_fd_open_or_create(file,
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen FALSE) < 0) {
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen mail_transaction_log_file_free(file);
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen file = NULL;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen }
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen } else {
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen file = mail_transaction_log_file_open_or_create(log, path);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (file == NULL) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* fallback to in-memory indexes */
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if (mail_index_move_to_memory(index) < 0) {
b66412da78711db8423288847ecfb08469609a03Timo Sirainen mail_transaction_log_close(&log);
b66412da78711db8423288847ecfb08469609a03Timo Sirainen return NULL;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen }
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen file = mail_transaction_log_file_open_or_create(log, path);
daccffdfe982210ab0c020e394818c20972c6b63Timo Sirainen i_assert(file != NULL);
daccffdfe982210ab0c020e394818c20972c6b63Timo Sirainen }
daccffdfe982210ab0c020e394818c20972c6b63Timo Sirainen file->refcount++;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen log->head = file;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen i_assert(log->files != NULL);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen if (index->fd != -1 &&
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen INDEX_HAS_MISSING_LOGS(index, log->head)) {
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen /* head log file isn't same as head index file -
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen shouldn't happen except in race conditions.
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen lock them and check again */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (mail_transaction_log_check_file_seq(log) < 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_transaction_log_close(&log);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen return NULL;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen return log;
901d0f036826476cf75799a0fdda5777e51301e4Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenstruct mail_transaction_log *
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenmail_transaction_log_open_or_create(struct mail_index *index)
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen{
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen return mail_transaction_log_open_int(index, FALSE);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen}
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenstruct mail_transaction_log *
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenmail_transaction_log_create(struct mail_index *index)
3320f4770d1f6c2cdd10f3c4ca5a324beb335339Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return mail_transaction_log_open_int(index, TRUE);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen}
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenvoid mail_transaction_log_close(struct mail_transaction_log **_log)
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen struct mail_transaction_log *log = *_log;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_transaction_log_views_close(log);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
ac8693e6a363204da30fdbc8c2fb47bb46a5d87aTimo Sirainen if (log->head != NULL)
bfe4a97ad6731012202b830c1219a4c10f91d72cTimo Sirainen log->head->refcount--;
ac8693e6a363204da30fdbc8c2fb47bb46a5d87aTimo Sirainen mail_transaction_logs_clean(log);
ac8693e6a363204da30fdbc8c2fb47bb46a5d87aTimo Sirainen i_assert(log->files == NULL);
ac8693e6a363204da30fdbc8c2fb47bb46a5d87aTimo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen *_log = NULL;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen log->index->log = NULL;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen i_free(log);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen}
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainenint mail_transaction_log_move_to_memory(struct mail_transaction_log *log)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen struct mail_transaction_log_file *file = log->head;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if (file == NULL || MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file))
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen return 0;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* read the whole file to memory. we might currently be appending
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen data into it, so we want to read it up to end of file */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file->buffer_offset = 0;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (file->buffer != NULL) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen buffer_free(file->buffer);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file->buffer = NULL;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (file->mmap_base != NULL) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (munmap(file->mmap_base, file->mmap_size) < 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file->filepath,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen "munmap()");
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file->mmap_base = NULL;
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen }
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen if (mail_transaction_log_file_read(file, 0) <= 0)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return -1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* after we've read the file into memory, make it into in-memory
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen log file */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (close(file->fd) < 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
1f6c210c30992e95b806d2f517e2b3625ed941c5Timo Sirainen file->filepath, "close()");
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file->fd = -1;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen return 0;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen}
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainenstatic int
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainenmail_transaction_log_file_read_hdr(struct mail_transaction_log_file *file,
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen int head, bool ignore_estale)
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen{
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen struct mail_transaction_log_file *f;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen int ret;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen i_assert(!MAIL_INDEX_IS_IN_MEMORY(file->log->index));
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen ret = pread_full(file->fd, &file->hdr, sizeof(file->hdr), 0);
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen if (ret < 0) {
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (errno != ESTALE || !ignore_estale) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file->filepath,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen "pread_full()");
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return -1;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (ret == 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_transaction_log_file_set_corrupted(file,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen "unexpected end of file while reading header");
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return 0;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen if (file->hdr.major_version != MAIL_TRANSACTION_LOG_MAJOR_VERSION) {
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen /* incompatible version - fix silently */
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen return 0;
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen }
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen if (file->hdr.hdr_size < MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_transaction_log_file_set_corrupted(file,
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen "Header size too small");
d6cc34b076dced6ebf8af47d72c8242357288312Timo Sirainen return 0;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen if (file->hdr.hdr_size < sizeof(file->hdr)) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* @UNSAFE: smaller than we expected - zero out the fields we
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen shouldn't have filled */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen memset(PTR_OFFSET(&file->hdr, file->hdr.hdr_size), 0,
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen sizeof(file->hdr) - file->hdr.hdr_size);
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if (file->hdr.indexid == 0) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen /* corrupted */
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen mail_index_set_error(file->log->index,
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen "Transaction log file %s: marked corrupted",
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen file->filepath);
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen return 0;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen }
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen if (file->hdr.indexid != file->log->index->indexid) {
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen if (file->log->index->fd != -1) {
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen /* index file was probably just rebuilt and we don't
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen know about it yet */
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen mail_transaction_log_file_set_corrupted(file,
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen "invalid indexid (%u != %u)",
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen file->hdr.indexid, file->log->index->indexid);
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen return 0;
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen }
bfe4a97ad6731012202b830c1219a4c10f91d72cTimo Sirainen
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen /* creating index file. since transaction log is created
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainen first, use the indexid in it to create the main index
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen to avoid races. */
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen file->log->index->indexid = file->hdr.indexid;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen }
b66412da78711db8423288847ecfb08469609a03Timo Sirainen
b66412da78711db8423288847ecfb08469609a03Timo Sirainen /* make sure we already don't have a file with the same sequence
b66412da78711db8423288847ecfb08469609a03Timo Sirainen opened. it shouldn't happen unless the old log file was
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen corrupted.
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
61b0637759146621cbb7edcbd0b03a71cfd66dfeTimo Sirainen If we're opening head log file, make sure the sequence is larger
bfe4a97ad6731012202b830c1219a4c10f91d72cTimo Sirainen than any existing one. */
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if (head) {
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen for (f = file->log->files; f != NULL; f = f->next) {
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen if (f->hdr.file_seq >= file->hdr.file_seq) {
206ac4273fa102500fa017f0c21a4fd72e94665aTimo Sirainen mail_transaction_log_file_set_corrupted(file,
206ac4273fa102500fa017f0c21a4fd72e94665aTimo Sirainen "invalid new transaction log sequence "
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen "(%u >= %u)",
b66412da78711db8423288847ecfb08469609a03Timo Sirainen f->hdr.file_seq, file->hdr.file_seq);
b66412da78711db8423288847ecfb08469609a03Timo Sirainen return 0;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen }
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen }
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen } else {
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen for (f = file->log->files; f != NULL; f = f->next) {
bfe4a97ad6731012202b830c1219a4c10f91d72cTimo Sirainen if (f->hdr.file_seq == file->hdr.file_seq) {
bfe4a97ad6731012202b830c1219a4c10f91d72cTimo Sirainen mail_transaction_log_file_set_corrupted(file,
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen "old transaction log already opened "
82b990b0bb2a1dad5c2634a508a5ad87715db402Timo Sirainen "(%u == %u)",
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen f->hdr.file_seq, file->hdr.file_seq);
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen return 0;
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen }
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen }
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen }
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen return 1;
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen}
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen
4babe70b863c71ea330cbf32ac0b71876f4f9137Timo Sirainenstatic int
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainenmail_transaction_log_init_hdr(struct mail_transaction_log *log,
f60ed9d5330a6167693b047d8138769fb596d41cTimo Sirainen struct mail_transaction_log_header *hdr)
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen{
0df9428baed48afaff90b4d4f03792d2fd756a43Timo Sirainen struct mail_index *index = log->index;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen unsigned int lock_id;
cb1fd563e6000153d1be76fd8722a096bd144b77Timo Sirainen
b66412da78711db8423288847ecfb08469609a03Timo Sirainen memset(hdr, 0, sizeof(*hdr));
b66412da78711db8423288847ecfb08469609a03Timo Sirainen hdr->major_version = MAIL_TRANSACTION_LOG_MAJOR_VERSION;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen hdr->minor_version = MAIL_TRANSACTION_LOG_MINOR_VERSION;
b66412da78711db8423288847ecfb08469609a03Timo Sirainen hdr->hdr_size = sizeof(struct mail_transaction_log_header);
b66412da78711db8423288847ecfb08469609a03Timo Sirainen hdr->indexid = log->index->indexid;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen hdr->create_stamp = ioloop_time;
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen
b66412da78711db8423288847ecfb08469609a03Timo Sirainen if (index->fd != -1) {
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen /* not creating index - make sure we have latest header */
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if (mail_index_lock_shared(index, TRUE, &lock_id) < 0)
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen return -1;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen if (mail_index_map(index, FALSE) <= 0) {
b66412da78711db8423288847ecfb08469609a03Timo Sirainen mail_index_unlock(index, lock_id);
b58fbcc79c40f867eccae98548fcd25a16823433Timo Sirainen return -1;
df02611c44e9432e7961223bf9bfa3fb233b1789Timo Sirainen }
23fdad6c7e2581921f511e24cd9371c9eaebcef9Timo Sirainen }
23fdad6c7e2581921f511e24cd9371c9eaebcef9Timo Sirainen hdr->prev_file_seq = index->hdr->log_file_seq;
hdr->prev_file_offset = index->hdr->log_file_int_offset;
hdr->file_seq = index->hdr->log_file_seq+1;
if (index->fd != -1)
mail_index_unlock(index, lock_id);
if (log->head != NULL && hdr->file_seq <= log->head->hdr.file_seq) {
/* make sure the sequence grows */
hdr->file_seq = log->head->hdr.file_seq+1;
}
return 0;
}
static int
mail_transaction_log_file_create2(struct mail_transaction_log_file *file,
bool lock, int new_fd,
struct dotlock **dotlock,
dev_t dev, ino_t ino, uoff_t file_size)
{
struct mail_index *index = file->log->index;
struct mail_transaction_log_header hdr;
struct stat st;
const char *path2;
int old_fd, ret;
bool rename_existing;
i_assert(!lock || file->log->head->locked);
/* log creation is locked now - see if someone already created it */
if (lock) {
/* don't even bother checking the existing file, but rename it
if it exists */
rename_existing = TRUE;
} else if ((old_fd = nfs_safe_open(file->filepath, O_RDWR)) != -1) {
if ((ret = fstat(old_fd, &st)) < 0) {
mail_index_file_set_syscall_error(index, file->filepath,
"fstat()");
} else if (st.st_ino == ino && CMP_DEV_T(st.st_dev, dev) &&
(uoff_t)st.st_size == file_size) {
/* same file, still broken */
} else {
/* file changed, use the new file */
(void)file_dotlock_delete(dotlock);
file->fd = old_fd;
return 0;
}
(void)close(old_fd);
old_fd = -1;
if (ret < 0) {
/* fstat() failure, return after closing fd.. */
return -1;
}
rename_existing = TRUE;
} else if (errno != ENOENT) {
mail_index_file_set_syscall_error(index, file->filepath,
"open()");
return -1;
} else {
rename_existing = FALSE;
}
if (mail_transaction_log_init_hdr(file->log, &hdr) < 0)
return -1;
if (write_full(new_fd, &hdr, sizeof(hdr)) < 0) {
mail_index_file_set_syscall_error(index, file->filepath,
"write_full()");
return -1;
}
if (lock) {
file->fd = new_fd;
ret = mail_transaction_log_file_lock(file);
file->fd = -1;
if (ret < 0)
return -1;
}
/* keep two log files */
if (rename_existing) {
/* rename() would be nice and easy way to do this, except then
there's a race condition between the rename and
file_dotlock_replace(). during that time the log file
doesn't exist, which could cause problems. */
path2 = t_strconcat(file->filepath, ".2", NULL);
if (unlink(path2) < 0 && errno != ENOENT) {
mail_index_set_error(index, "unlink(%s) failed: %m",
path2);
/* try to link() anyway */
}
if (link(file->filepath, path2) < 0 &&
errno != ENOENT && errno != EEXIST) {
mail_index_set_error(index, "link(%s, %s) failed: %m",
file->filepath, path2);
/* ignore the error. we don't care that much about the
second log file and we're going to overwrite this
first one. */
}
}
if (file_dotlock_replace(dotlock,
DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) <= 0)
return -1;
/* success */
file->fd = new_fd;
return 0;
}
static int
mail_transaction_log_file_create(struct mail_transaction_log_file *file,
bool lock, dev_t dev, ino_t ino,
uoff_t file_size)
{
struct mail_index *index = file->log->index;
struct dotlock *dotlock;
struct stat st;
mode_t old_mask;
int fd;
i_assert(!MAIL_INDEX_IS_IN_MEMORY(index));
if (stat(index->dir, &st) < 0) {
if (ENOTFOUND(errno)) {
/* the whole index directory was deleted, which means
the mailbox was deleted by another process.
fail silently. */
mail_index_mark_corrupted(index);
return -1;
}
mail_index_file_set_syscall_error(index, index->dir, "stat()");
return -1;
}
/* With dotlocking we might already have path.lock created, so this
filename has to be different. */
old_mask = umask(index->mode ^ 0666);
fd = file_dotlock_open(&file->log->new_dotlock_settings,
file->filepath, 0, &dotlock);
umask(old_mask);
if (fd == -1) {
mail_index_file_set_syscall_error(index, file->filepath,
"file_dotlock_open()");
return -1;
}
if (index->gid != (gid_t)-1 &&
fchown(fd, (uid_t)-1, index->gid) < 0) {
mail_index_file_set_syscall_error(index, file->filepath,
"fchown()");
(void)file_dotlock_delete(&dotlock);
return -1;
}
/* either fd gets used or the dotlock gets deleted and returned fd
is for the existing file */
if (mail_transaction_log_file_create2(file, lock, fd, &dotlock,
dev, ino, file_size) < 0) {
if (dotlock != NULL)
(void)file_dotlock_delete(&dotlock);
return -1;
}
return 0;
}
static void
mail_transaction_log_file_add_to_head(struct mail_transaction_log_file *file)
{
struct mail_transaction_log *log = file->log;
struct mail_transaction_log_file **p;
if (log->index->map != NULL &&
file->hdr.file_seq == log->index->map->hdr.log_file_seq &&
log->index->map->hdr.log_file_int_offset != 0) {
/* we can get a valid log offset from index file. initialize
sync_offset from it so we don't have to read the whole log
file from beginning. */
file->sync_offset = log->index->map->hdr.log_file_int_offset;
} else {
file->sync_offset = file->hdr.hdr_size;
}
/* append to end of list. */
for (p = &log->files; *p != NULL; p = &(*p)->next)
i_assert((*p)->hdr.file_seq < file->hdr.file_seq);
*p = file;
}
static void
mail_transaction_log_file_add_to_list(struct mail_transaction_log_file *file)
{
struct mail_transaction_log *log = file->log;
struct mail_transaction_log_file **p;
file->sync_offset = file->hdr.hdr_size;
/* insert it to correct position */
for (p = &log->files; *p != NULL; p = &(*p)->next) {
i_assert((*p)->hdr.file_seq != file->hdr.file_seq);
if ((*p)->hdr.file_seq > file->hdr.file_seq)
break;
}
file->next = *p;
*p = file;
}
static int
mail_transaction_log_file_fd_open(struct mail_transaction_log_file *file,
bool head, bool ignore_estale)
{
struct stat st;
i_assert(!MAIL_INDEX_IS_IN_MEMORY(file->log->index));
if (fstat(file->fd, &st) < 0) {
if (errno != ESTALE || !ignore_estale) {
mail_index_file_set_syscall_error(file->log->index,
file->filepath,
"fstat()");
}
return -1;
}
file->st_dev = st.st_dev;
file->st_ino = st.st_ino;
file->last_mtime = st.st_mtime;
file->last_size = st.st_size;
return mail_transaction_log_file_read_hdr(file, head, ignore_estale);
}
static int
mail_transaction_log_file_fd_open_or_create(struct mail_transaction_log_file
*file, bool try_retry)
{
int ret;
ret = mail_transaction_log_file_fd_open(file, TRUE, !try_retry);
if (ret == 0) {
/* corrupted header, recreate the file */
if (mail_transaction_log_file_create(file, FALSE,
file->st_dev,
file->st_ino,
file->last_size) < 0)
ret = -1;
else {
ret = mail_transaction_log_file_fd_open(file, TRUE,
FALSE);
if (ret == 0) {
/* newly created transaction log corrupted */
return -1;
}
}
}
if (ret < 0)
return errno == ENOENT && try_retry ? 0 : -1;
mail_transaction_log_file_add_to_head(file);
return 1;
}
static struct mail_transaction_log_file *
mail_transaction_log_file_alloc_in_memory(struct mail_transaction_log *log)
{
struct mail_transaction_log_file *file;
file = i_new(struct mail_transaction_log_file, 1);
file->log = log;
file->filepath = i_strdup("(in-memory transaction log file)");
file->fd = -1;
if (mail_transaction_log_init_hdr(log, &file->hdr) < 0) {
i_free(file);
return NULL;
}
file->buffer = buffer_create_dynamic(default_pool, 4096);
file->buffer_offset = sizeof(file->hdr);
mail_transaction_log_file_add_to_head(file);
return file;
}
static struct mail_transaction_log_file *
mail_transaction_log_file_open_or_create(struct mail_transaction_log *log,
const char *path)
{
struct mail_transaction_log_file *file;
unsigned int i;
int ret;
if (MAIL_INDEX_IS_IN_MEMORY(log->index))
return mail_transaction_log_file_alloc_in_memory(log);
file = mail_transaction_log_file_alloc(log, path);
for (i = 0; ; i++) {
file->fd = nfs_safe_open(path, O_RDWR);
if (file->fd == -1) {
if (errno != ENOENT) {
mail_index_file_set_syscall_error(log->index,
path,
"open()");
break;
}
/* doesn't exist, try creating it */
if (mail_transaction_log_file_create(file, FALSE,
0, 0, 0) < 0)
break;
}
ret = mail_transaction_log_file_fd_open_or_create(file,
i == MAIL_INDEX_ESTALE_RETRY_COUNT);
if (ret > 0)
return file;
if (ret < 0)
break;
/* ESTALE - retry */
}
mail_transaction_log_file_free(file);
return NULL;
}
static struct mail_transaction_log_file *
mail_transaction_log_file_open(struct mail_transaction_log *log,
const char *path)
{
struct mail_transaction_log_file *file;
unsigned int i;
int ret;
file = mail_transaction_log_file_alloc(log, path);
for (i = 0;; i++) {
file->fd = nfs_safe_open(path, O_RDWR);
if (file->fd == -1) {
mail_index_file_set_syscall_error(log->index, path,
"open()");
break;
}
ret = mail_transaction_log_file_fd_open(file,
TRUE, i < MAIL_INDEX_ESTALE_RETRY_COUNT);
if (ret > 0) {
/* success */
mail_transaction_log_file_add_to_head(file);
return file;
}
if (ret == 0) {
/* corrupted */
break;
}
if (errno != ESTALE ||
i == MAIL_INDEX_ESTALE_RETRY_COUNT) {
/* syscall error */
break;
}
/* ESTALE - try again */
}
mail_transaction_log_file_free(file);
return NULL;
}
void mail_transaction_logs_clean(struct mail_transaction_log *log)
{
struct mail_transaction_log_file *file, *next;
for (file = log->files; file != NULL; file = next) {
next = file->next;
i_assert(file->refcount >= 0);
if (file->refcount == 0)
mail_transaction_log_file_free(file);
}
}
int mail_transaction_log_rotate(struct mail_transaction_log *log, bool lock)
{
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 (lock)
file->locked = TRUE;
} 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, path,
"fstat()");
return -1;
}
file = mail_transaction_log_file_alloc(log, path);
if (mail_transaction_log_file_create(file, lock, st.st_dev,
st.st_ino,
st.st_size) < 0) {
mail_transaction_log_file_free(file);
return -1;
}
ret = mail_transaction_log_file_fd_open_or_create(file, FALSE);
if (ret <= 0) {
i_assert(ret != 0);
mail_transaction_log_file_free(file);
return -1;
}
}
i_assert(file->locked == lock);
if (--log->head->refcount == 0)
mail_transaction_logs_clean(log);
else
mail_transaction_log_file_unlock(log->head);
i_assert(log->head != file);
i_assert(log->files != NULL);
log->head = file;
log->head->refcount++;
return 0;
}
static int mail_transaction_log_refresh(struct mail_transaction_log *log,
bool create_if_needed)
{
struct mail_transaction_log_file *file;
struct stat st;
const char *path;
i_assert(log->head != NULL);
if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(log->head))
return 0;
path = t_strconcat(log->index->filepath,
MAIL_TRANSACTION_LOG_SUFFIX, NULL);
if (nfs_safe_stat(path, &st) < 0) {
if (errno != ENOENT) {
mail_index_file_set_syscall_error(log->index, path,
"stat()");
return -1;
}
/* log was deleted. just reopen/recreate it. */
} else {
if (log->head->st_ino == st.st_ino &&
CMP_DEV_T(log->head->st_dev, st.st_dev)) {
/* same file */
return 0;
}
}
file = create_if_needed ?
mail_transaction_log_file_open_or_create(log, path) :
mail_transaction_log_file_open(log, path);
if (file == NULL)
return -1;
i_assert(!file->locked);
if (--log->head->refcount == 0)
mail_transaction_logs_clean(log);
i_assert(log->files != NULL);
log->head = file;
log->head->refcount++;
return 0;
}
int mail_transaction_log_file_find(struct mail_transaction_log *log,
uint32_t file_seq,
struct mail_transaction_log_file **file_r)
{
struct mail_transaction_log_file *file;
struct stat st;
const char *path;
int ret, fd;
if (file_seq > log->head->hdr.file_seq) {
/* don't try to recreate log file if it gets lost. we're
already in trouble and with mmap_disable the creation
could cause a recursive mail_index_map() call */
if (mail_transaction_log_refresh(log, FALSE) < 0)
return -1;
}
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 */
path = t_strconcat(log->index->filepath,
MAIL_TRANSACTION_LOG_SUFFIX".2", NULL);
fd = nfs_safe_open(path, O_RDWR);
if (fd == -1) {
if (errno == ENOENT)
return 0;
mail_index_file_set_syscall_error(log->index, path, "open()");
return -1;
}
if (fstat(fd, &st) < 0) {
close_keep_errno(fd);
if (errno == ESTALE) {
/* treat as "doesn't exist" */
return 0;
}
mail_index_file_set_syscall_error(log->index, path, "fstat()");
return -1;
}
/* see if we have it already opened */
for (file = log->files; file != NULL; file = file->next) {
if (file->st_ino == st.st_ino &&
CMP_DEV_T(file->st_dev, st.st_dev)) {
if (close(fd) < 0)
i_error("close() failed: %m");
return 0;
}
}
file = mail_transaction_log_file_alloc(log, path);
file->fd = fd;
ret = mail_transaction_log_file_fd_open(file, FALSE, TRUE);
if (ret <= 0) {
bool stale = errno == ESTALE;
if (ret == 0) {
/* corrupted, delete it */
if (unlink(file->filepath) < 0 && errno != ENOENT) {
i_error("unlink(%s) failed: %m",
file->filepath);
}
mail_transaction_log_file_free(file);
return 0;
}
mail_transaction_log_file_free(file);
if (stale) {
/* treat as "doesn't exist" */
return 0;
}
return -1;
}
/* got it */
mail_transaction_log_file_add_to_list(file);
/* but is it what we expected? */
if (file->hdr.file_seq != file_seq)
return 0;
*file_r = file;
return 1;
}
static int
mail_transaction_log_file_sync(struct mail_transaction_log_file *file)
{
const struct mail_transaction_header *hdr;
const void *data;
size_t size, avail;
uint32_t hdr_size = 0;
data = buffer_get_data(file->buffer, &size);
if (file->sync_offset < file->buffer_offset)
file->sync_offset = file->buffer_offset;
while (file->sync_offset - file->buffer_offset + sizeof(*hdr) <= size) {
hdr = CONST_PTR_OFFSET(data, file->sync_offset -
file->buffer_offset);
hdr_size = mail_index_offset_to_uint32(hdr->size);
if (hdr_size == 0) {
/* unfinished */
return 0;
}
if (hdr_size < sizeof(*hdr)) {
mail_transaction_log_file_set_corrupted(file,
"hdr.size too small (%u)", hdr_size);
return -1;
}
if (file->sync_offset - file->buffer_offset + hdr_size > size)
break;
file->sync_offset += hdr_size;
}
avail = file->sync_offset - file->buffer_offset;
if (avail != size && avail >= sizeof(*hdr)) {
/* record goes outside the file we've seen. or if
we're accessing the log file via unlocked mmaped
memory, it may be just that the memory was updated
after we checked the file size. */
if (file->locked || file->mmap_base == NULL) {
if (hdr_size != 0) {
mail_transaction_log_file_set_corrupted(file,
"hdr.size too large (%u)", hdr_size);
} else {
mail_transaction_log_file_set_corrupted(file,
"Unexpected garbage at EOF");
}
return -1;
}
}
return 0;
}
static int
mail_transaction_log_file_read(struct mail_transaction_log_file *file,
uoff_t offset)
{
void *data;
size_t size;
uint32_t read_offset;
int ret;
i_assert(file->mmap_base == NULL);
if (file->buffer != NULL && file->buffer_offset > offset) {
/* we have to insert missing data to beginning of buffer */
size = file->buffer_offset - offset;
buffer_copy(file->buffer, size, file->buffer, 0, (size_t)-1);
file->buffer_offset -= size;
data = buffer_get_space_unsafe(file->buffer, 0, size);
ret = pread_full(file->fd, data, size, offset);
if (ret == 0) {
mail_transaction_log_file_set_corrupted(file,
"Unexpected end of file");
return 0;
}
if (ret < 0) {
if (errno == ESTALE) {
/* log file was deleted in NFS server,
fail silently */
return 0;
}
mail_index_file_set_syscall_error(file->log->index,
file->filepath,
"pread()");
return -1;
}
}
if (file->buffer == NULL) {
file->buffer =
buffer_create_dynamic(default_pool, LOG_PREFETCH);
file->buffer_offset = offset;
}
/* read all records */
read_offset = file->buffer_offset + buffer_get_used_size(file->buffer);
do {
data = buffer_append_space_unsafe(file->buffer, LOG_PREFETCH);
ret = pread(file->fd, data, LOG_PREFETCH, read_offset);
if (ret > 0)
read_offset += ret;
size = read_offset - file->buffer_offset;
buffer_set_used_size(file->buffer, size);
} while (ret > 0 || (ret < 0 && errno == EINTR));
file->last_size = read_offset;
if (mail_transaction_log_file_sync(file) < 0)
return -1;
if (ret == 0) {
/* EOF */
i_assert(file->sync_offset >= file->buffer_offset);
buffer_set_used_size(file->buffer,
file->sync_offset - file->buffer_offset);
return 1;
}
if (errno == ESTALE) {
/* log file was deleted in NFS server, fail silently */
return 0;
}
mail_index_file_set_syscall_error(file->log->index, file->filepath,
"pread()");
return -1;
}
int mail_transaction_log_file_map(struct mail_transaction_log_file *file,
uoff_t start_offset, uoff_t end_offset)
{
struct mail_index *index = file->log->index;
size_t size;
struct stat st;
int ret, use_mmap;
i_assert(start_offset <= end_offset);
if (file->hdr.indexid == 0) {
/* corrupted */
return 0;
}
if (start_offset < file->hdr.hdr_size) {
mail_transaction_log_file_set_corrupted(file,
"offset (%"PRIuUOFF_T") < header size (%u)",
start_offset, file->hdr.hdr_size);
return -1;
}
if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file))
return 1;
/* with mmap_no_write we could alternatively just write to log with
msync() rather than pwrite(). but since there aren't many such OSes
left, it's easier to just use mmap_disable behavior with it */
use_mmap = !index->mmap_disable && !index->mmap_no_write;
if (file->buffer != NULL && file->buffer_offset <= start_offset) {
/* see if we already have it */
size = buffer_get_used_size(file->buffer);
if (file->buffer_offset + size >= end_offset)
return 1;
}
if (use_mmap) {
if (fstat(file->fd, &st) < 0) {
mail_index_file_set_syscall_error(index, file->filepath,
"fstat()");
return -1;
}
file->last_size = st.st_size;
if (start_offset > (uoff_t)st.st_size) {
mail_transaction_log_file_set_corrupted(file,
"start_offset (%"PRIuUOFF_T") > file size "
"(%"PRIuUOFF_T")", start_offset,
(uoff_t)st.st_size);
return -1;
}
if (file->mmap_base != NULL &&
(uoff_t)st.st_size == file->mmap_size &&
file->buffer_offset <= start_offset &&
end_offset == (uoff_t)-1) {
/* it's all mmaped already */
if (mail_transaction_log_file_sync(file) < 0)
return -1;
return 1;
}
}
if (file->buffer != NULL &&
(file->mmap_base != NULL || use_mmap)) {
buffer_free(file->buffer);
file->buffer = NULL;
}
if (file->mmap_base != NULL) {
if (munmap(file->mmap_base, file->mmap_size) < 0) {
mail_index_file_set_syscall_error(index, file->filepath,
"munmap()");
}
file->mmap_base = NULL;
}
if (!use_mmap) {
ret = mail_transaction_log_file_read(file, start_offset);
if (ret <= 0) {
/* make sure we don't leave ourself in
inconsistent state */
if (file->buffer != NULL) {
buffer_free(file->buffer);
file->buffer = NULL;
}
return ret;
}
} else {
file->mmap_size = st.st_size;
file->mmap_base = mmap(NULL, file->mmap_size, PROT_READ,
MAP_SHARED, file->fd, 0);
if (file->mmap_base == MAP_FAILED) {
file->mmap_base = NULL;
mail_index_file_set_syscall_error(index, file->filepath,
"mmap()");
return -1;
}
if (file->mmap_size > mmap_get_page_size()) {
if (madvise(file->mmap_base, file->mmap_size,
MADV_SEQUENTIAL) < 0) {
mail_index_file_set_syscall_error(index,
file->filepath, "madvise()");
}
}
file->buffer = buffer_create_const_data(default_pool,
file->mmap_base,
file->mmap_size);
file->buffer_offset = 0;
if (mail_transaction_log_file_sync(file) < 0)
return -1;
}
if (start_offset > file->sync_offset) {
mail_transaction_log_file_set_corrupted(file,
"start_offset (%"PRIuUOFF_T") > current sync_offset "
"(%"PRIuUOFF_T")", start_offset, file->sync_offset);
return -1;
}
if (end_offset != (uoff_t)-1 && end_offset > file->sync_offset) {
mail_transaction_log_file_set_corrupted(file,
"end_offset (%"PRIuUOFF_T") > current sync_offset "
"(%"PRIuUOFF_T")", end_offset, file->sync_offset);
return -1;
}
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)
{
i_assert(log->index->log_locked);
*file_seq_r = log->head->hdr.file_seq;
*file_offset_r = log->head->sync_offset;
}
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;
}