mail-transaction-log.c revision 5d04f56a6d953287f82dc20ac2f307d30deebf76
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (C) 2003-2004 Timo Sirainen */
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#include "lib.h"
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#include "ioloop.h"
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#include "buffer.h"
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#include "file-lock.h"
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#include "file-dotlock.h"
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#include "read-full.h"
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#include "write-full.h"
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#include "mmap-util.h"
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#include "mail-index-private.h"
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#include "mail-index-view-private.h"
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#include "mail-transaction-log-private.h"
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#include "mail-transaction-util.h"
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch#include "mail-index-transaction-private.h"
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch#include <stddef.h>
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch#include <sys/stat.h>
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch/* this lock should never exist for a long time.. */
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#define LOG_DOTLOCK_TIMEOUT 30
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#define LOG_DOTLOCK_STALE_TIMEOUT 0
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch#define LOG_DOTLOCK_IMMEDIATE_STALE_TIMEOUT 120
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Boschstruct mail_transaction_add_ctx {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch struct mail_transaction_log *log;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch struct mail_index_view *view;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch buffer_t *appends, *expunges;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch buffer_t *flag_updates, *cache_updates;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch};
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Boschstatic struct mail_transaction_log_file *
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Boschmail_transaction_log_file_open_or_create(struct mail_transaction_log *log,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch const char *path);
10962368c30afde135743fd9796122e88a708e87Stephan Boschstatic int mail_transaction_log_rotate(struct mail_transaction_log *log);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Boschstatic int
10962368c30afde135743fd9796122e88a708e87Stephan Boschmail_transaction_log_file_lock(struct mail_transaction_log_file *file,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch int lock_type);
10962368c30afde135743fd9796122e88a708e87Stephan Boschstatic int mail_transaction_log_lock_head(struct mail_transaction_log *log);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Boschvoid
10962368c30afde135743fd9796122e88a708e87Stephan Boschmail_transaction_log_file_set_corrupted(struct mail_transaction_log_file *file,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch const char *fmt, ...)
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch{
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch va_list va;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->hdr.indexid = 0;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (pwrite_full(file->fd, &file->hdr.indexid,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch sizeof(file->hdr.indexid), 0) < 0) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch mail_index_file_set_syscall_error(file->log->index,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch file->filepath, "pwrite()");
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch va_start(va, fmt);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch t_push();
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch mail_index_set_error(file->log->index,
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch "Corrupted transaction log file %s: %s",
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->filepath, t_strdup_vprintf(fmt, va));
10962368c30afde135743fd9796122e88a708e87Stephan Bosch t_pop();
10962368c30afde135743fd9796122e88a708e87Stephan Bosch va_end(va);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch}
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch#define INDEX_HAS_MISSING_LOGS(index, file) \
10962368c30afde135743fd9796122e88a708e87Stephan Bosch !(((file)->hdr.file_seq == (index)->hdr->log_file_seq && \
10962368c30afde135743fd9796122e88a708e87Stephan Bosch (index)->hdr->log_file_offset >= \
10962368c30afde135743fd9796122e88a708e87Stephan Bosch sizeof(struct mail_transaction_log_header)) || \
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch ((file)->hdr.prev_file_seq == (index)->hdr->log_file_seq && \
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch (file)->hdr.prev_file_offset == (index)->hdr->log_file_offset))
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Boschstatic int mail_transaction_log_check_file_seq(struct mail_transaction_log *log)
10962368c30afde135743fd9796122e88a708e87Stephan Bosch{
10962368c30afde135743fd9796122e88a708e87Stephan Bosch struct mail_index *index = log->index;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch struct mail_transaction_log_file *file;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch unsigned int lock_id;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch int ret;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (mail_transaction_log_lock_head(log) < 0)
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return -1;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch file = log->head;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch file->refcount++;
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch ret = mail_index_lock_shared(index, TRUE, &lock_id);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (ret == 0) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch ret = mail_index_map(index, FALSE);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (ret <= 0)
10962368c30afde135743fd9796122e88a708e87Stephan Bosch ret = -1;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch else if (INDEX_HAS_MISSING_LOGS(index, file)) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch /* broken - fix it by creating a new log file */
10962368c30afde135743fd9796122e88a708e87Stephan Bosch ret = mail_transaction_log_rotate(log);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (--file->refcount == 0)
10962368c30afde135743fd9796122e88a708e87Stephan Bosch mail_transaction_logs_clean(log);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch else
10962368c30afde135743fd9796122e88a708e87Stephan Bosch (void)mail_transaction_log_file_lock(file, F_UNLCK);
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch return ret;
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch}
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Boschstruct mail_transaction_log *
10962368c30afde135743fd9796122e88a708e87Stephan Boschmail_transaction_log_open_or_create(struct mail_index *index)
10962368c30afde135743fd9796122e88a708e87Stephan Bosch{
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch struct mail_transaction_log *log;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch const char *path;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch log = i_new(struct mail_transaction_log, 1);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch log->index = index;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch path = t_strconcat(log->index->filepath,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch MAIL_TRANSACTION_LOG_PREFIX, NULL);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch log->head = mail_transaction_log_file_open_or_create(log, path);
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch if (log->head == NULL) {
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch i_free(log);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch return NULL;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch }
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (index->fd != -1 &&
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch INDEX_HAS_MISSING_LOGS(index, log->head)) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch /* head log file isn't same as head index file -
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch shouldn't happen except in race conditions. lock them and
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch check again - FIXME: missing error handling. */
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch (void)mail_transaction_log_check_file_seq(log);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch }
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch return log;
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch}
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Boschvoid mail_transaction_log_close(struct mail_transaction_log *log)
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch{
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch mail_transaction_log_views_close(log);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch log->head->refcount--;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch mail_transaction_logs_clean(log);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch log->index->log = NULL;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch i_free(log);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch}
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Boschstatic int
1faa520084b901b15d83d3d68baaee2535051defStephan Boschmail_transaction_log_file_dotlock(struct mail_transaction_log_file *file,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch int lock_type)
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch{
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch int ret;
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch if (lock_type == F_UNLCK) {
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch file->lock_type = F_UNLCK;
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch if (--file->log->dotlock_count > 0)
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch return 0;
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch ret = file_unlock_dotlock(file->filepath, &file->log->dotlock);
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch if (ret < 0) {
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch mail_index_file_set_syscall_error(file->log->index,
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch file->filepath, "file_unlock_dotlock()");
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch return -1;
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (ret == 0) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch mail_index_set_error(file->log->index,
ba592dc74a004ad47dfe58edcfc1ca7297551e39Phil Carmody "Dotlock was lost for transaction log file %s",
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->filepath);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return -1;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return 0;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (file->log->dotlock_count > 0)
10962368c30afde135743fd9796122e88a708e87Stephan Bosch ret = 1;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch else {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch ret = file_lock_dotlock(file->filepath, NULL, FALSE,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch LOG_DOTLOCK_TIMEOUT,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch LOG_DOTLOCK_STALE_TIMEOUT,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch LOG_DOTLOCK_IMMEDIATE_STALE_TIMEOUT,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch NULL, NULL, &file->log->dotlock);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (ret > 0) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->log->dotlock_count++;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->lock_type = F_WRLCK;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return 0;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (ret < 0) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch mail_index_file_set_syscall_error(file->log->index,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->filepath,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch "file_lock_dotlock()");
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return -1;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch mail_index_set_error(file->log->index,
b9ee73a064b38d8aeec754b964cc34b23487387aTimo Sirainen "Timeout while waiting for release of "
10962368c30afde135743fd9796122e88a708e87Stephan Bosch "dotlock for transaction log file %s",
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->filepath);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->log->index->index_lock_timeout = TRUE;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return -1;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch}
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Boschstatic int
10962368c30afde135743fd9796122e88a708e87Stephan Boschmail_transaction_log_file_lock(struct mail_transaction_log_file *file,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch int lock_type)
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch{
10962368c30afde135743fd9796122e88a708e87Stephan Bosch int ret;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (lock_type == F_UNLCK) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch i_assert(file->lock_type != F_UNLCK);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch } else {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch i_assert(file->lock_type == F_UNLCK);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
b9ee73a064b38d8aeec754b964cc34b23487387aTimo Sirainen
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (file->log->index->fcntl_locks_disable)
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return mail_transaction_log_file_dotlock(file, lock_type);
5968fa8151eecd191b1973b44dd8bec9b75810a6Phil Carmody
b9ee73a064b38d8aeec754b964cc34b23487387aTimo Sirainen ret = file_wait_lock_full(file->fd, lock_type, DEFAULT_LOCK_TIMEOUT,
5968fa8151eecd191b1973b44dd8bec9b75810a6Phil Carmody NULL, NULL);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (ret > 0) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->lock_type = lock_type;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return 0;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (ret < 0) {
378e6cb162b355d6f103526505bc00b9a78962e7Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->filepath,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch "file_wait_lock()");
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return -1;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch mail_index_set_error(file->log->index,
b9ee73a064b38d8aeec754b964cc34b23487387aTimo Sirainen "Timeout while waiting for release of "
10962368c30afde135743fd9796122e88a708e87Stephan Bosch "%s fcntl() lock for transaction log file %s",
10962368c30afde135743fd9796122e88a708e87Stephan Bosch lock_type == F_WRLCK ? "exclusive" : "shared",
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->filepath);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->log->index->index_lock_timeout = TRUE;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return -1;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch}
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Boschstatic void
10962368c30afde135743fd9796122e88a708e87Stephan Boschmail_transaction_log_file_close(struct mail_transaction_log_file *file)
10962368c30afde135743fd9796122e88a708e87Stephan Bosch{
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (file->buffer != NULL)
10962368c30afde135743fd9796122e88a708e87Stephan Bosch buffer_free(file->buffer);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (file->mmap_base != NULL) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (munmap(file->mmap_base, file->mmap_size) < 0) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch mail_index_file_set_syscall_error(file->log->index,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch file->filepath,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch "munmap()");
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch }
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (close(file->fd) < 0) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch mail_index_file_set_syscall_error(file->log->index,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch file->filepath, "close()");
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch }
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch i_free(file->filepath);
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch i_free(file);
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch}
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Boschstatic int
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Boschmail_transaction_log_file_read_hdr(struct mail_transaction_log_file *file,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch struct stat *st)
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch{
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch int ret;
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch uint32_t old_size = file->hdr.used_size;
f74dbd3ff682fea040f60383e001620d1f1b09d3Stephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (file->lock_type != F_UNLCK)
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch ret = pread_full(file->fd, &file->hdr, sizeof(file->hdr), 0);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch else {
f883bf3eff62f5d27df5ee9ee664edc38a77937fStephan Bosch if (mail_transaction_log_file_lock(file, F_RDLCK) < 0)
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch return -1;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch /* we have to fstat() again since it may have changed after
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch locking. */
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (fstat(file->fd, st) < 0) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch mail_index_file_set_syscall_error(file->log->index,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch file->filepath,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch "fstat()");
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch (void)mail_transaction_log_file_lock(file, F_UNLCK);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return -1;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch ret = pread_full(file->fd, &file->hdr,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch sizeof(file->hdr), 0);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch (void)mail_transaction_log_file_lock(file, F_UNLCK);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch file->last_mtime = st->st_mtime;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (ret < 0) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch // FIXME: handle ESTALE
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch mail_index_file_set_syscall_error(file->log->index,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch file->filepath,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch "pread_full()");
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return -1;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (ret == 0) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch mail_transaction_log_file_set_corrupted(file,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch "unexpected end of file while reading header");
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return 0;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (file->hdr.indexid == 0) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch /* corrupted */
10962368c30afde135743fd9796122e88a708e87Stephan Bosch mail_index_set_error(file->log->index,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch "Transaction log file %s: marked corrupted",
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->filepath);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return 0;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (file->hdr.indexid != file->log->index->indexid &&
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->log->index->indexid != 0) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch /* either index was just recreated, or transaction has wrong
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch indexid. we don't know here which one is the case, so we'll
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch just fail. If index->indexid == 0, we're rebuilding it and
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch we just want to lock the transaction log. */
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch mail_index_set_error(file->log->index,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch "Transaction log file %s: invalid indexid",
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch file->filepath);
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch return 0;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch }
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (file->hdr.used_size > st->st_size) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch mail_transaction_log_file_set_corrupted(file,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch "used_size (%u) > file size (%"PRIuUOFF_T")",
ba592dc74a004ad47dfe58edcfc1ca7297551e39Phil Carmody file->hdr.used_size, (uoff_t)st->st_size);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch return 0;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch }
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (file->hdr.used_size < old_size) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch mail_transaction_log_file_set_corrupted(file,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch "used_size (%u) < old_size (%u)",
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch file->hdr.used_size, old_size);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch return 0;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch }
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch return 1;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch}
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Boschstatic int
1faa520084b901b15d83d3d68baaee2535051defStephan Boschmail_transaction_log_file_create(struct mail_transaction_log *log,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch const char *path, dev_t dev, ino_t ino)
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch{
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch#define LOG_NEW_DOTLOCK_SUFFIX ".newlock"
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch struct mail_index *index = log->index;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch struct mail_transaction_log_header hdr;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch struct stat st;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch int fd, fd2, ret;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch /* With dotlocking we might already have path.lock created, so this
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch filename has to be different. */
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch fd = file_dotlock_open(path, NULL, LOG_NEW_DOTLOCK_SUFFIX,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch LOG_DOTLOCK_TIMEOUT,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch LOG_DOTLOCK_STALE_TIMEOUT,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch LOG_DOTLOCK_IMMEDIATE_STALE_TIMEOUT, NULL, NULL);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (fd == -1) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch mail_index_file_set_syscall_error(index, path,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch "file_dotlock_open()");
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch return -1;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch }
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch /* log creation is locked now - see if someone already created it */
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch fd2 = open(path, O_RDWR);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (fd2 != -1) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if ((ret = fstat(fd2, &st)) < 0) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch mail_index_file_set_syscall_error(index, path,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch "fstat()");
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch } else if (st.st_dev == dev && st.st_ino == ino) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch /* same file, still broken */
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch } else {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch (void)file_dotlock_delete(path, LOG_NEW_DOTLOCK_SUFFIX,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch fd2);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch return fd2;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch }
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch (void)close(fd2);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch fd2 = -1;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (ret < 0)
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch return -1;
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch } else if (errno != ENOENT) {
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch mail_index_file_set_syscall_error(index, path, "open()");
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch return -1;
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch }
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch memset(&hdr, 0, sizeof(hdr));
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch hdr.indexid = index->indexid;
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch hdr.used_size = sizeof(hdr);
6e62aa36a3190ef7193bd86158a4245da49132f0Stephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (index->fd != -1) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch hdr.prev_file_seq = index->hdr->log_file_seq;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch hdr.prev_file_offset = index->hdr->log_file_offset;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
ba592dc74a004ad47dfe58edcfc1ca7297551e39Phil Carmody hdr.file_seq = index->hdr->log_file_seq+1;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (log->head != NULL && hdr.file_seq <= log->head->hdr.file_seq) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch /* make sure the sequence grows */
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch hdr.file_seq = log->head->hdr.file_seq+1;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch }
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (write_full(fd, &hdr, sizeof(hdr)) < 0) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch mail_index_file_set_syscall_error(index, path,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch "write_full()");
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch (void)file_dotlock_delete(path, LOG_NEW_DOTLOCK_SUFFIX, fd);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch return -1;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch }
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch fd2 = dup(fd);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (fd2 < 0) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch mail_index_file_set_syscall_error(index, path, "dup()");
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch (void)file_dotlock_delete(path, LOG_NEW_DOTLOCK_SUFFIX, fd);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch return -1;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch }
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (file_dotlock_replace(path, LOG_NEW_DOTLOCK_SUFFIX, fd, FALSE) <= 0)
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch return -1;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch /* success */
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch return fd2;
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch}
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Boschstatic struct mail_transaction_log_file *
10962368c30afde135743fd9796122e88a708e87Stephan Boschmail_transaction_log_file_fd_open(struct mail_transaction_log *log,
10962368c30afde135743fd9796122e88a708e87Stephan Bosch const char *path, int fd)
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch{
10962368c30afde135743fd9796122e88a708e87Stephan Bosch struct mail_transaction_log_file **p;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch struct mail_transaction_log_file *file;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch struct stat st;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch int ret;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (fstat(fd, &st) < 0) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch mail_index_file_set_syscall_error(log->index, path, "stat()");
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return NULL;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch file = i_new(struct mail_transaction_log_file, 1);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->refcount = 1;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch file->log = log;
5968fa8151eecd191b1973b44dd8bec9b75810a6Phil Carmody file->filepath = i_strdup(path);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->fd = fd;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->lock_type = F_UNLCK;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->st_dev = st.st_dev;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch file->st_ino = st.st_ino;
ba592dc74a004ad47dfe58edcfc1ca7297551e39Phil Carmody
10962368c30afde135743fd9796122e88a708e87Stephan Bosch ret = mail_transaction_log_file_read_hdr(file, &st);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if (ret == 0) {
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch /* corrupted header */
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch fd = mail_transaction_log_file_create(log, path,
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch st.st_dev, st.st_ino);
1faa520084b901b15d83d3d68baaee2535051defStephan Bosch if (fd == -1)
10962368c30afde135743fd9796122e88a708e87Stephan Bosch ret = -1;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch else if (fstat(fd, &st) < 0) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch mail_index_file_set_syscall_error(log->index, path,
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch "stat()");
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch (void)close(fd);
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch fd = -1;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch ret = -1;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch }
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch if (fd != -1) {
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch (void)close(file->fd);
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch file->fd = fd;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch file->st_dev = st.st_dev;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch file->st_ino = st.st_ino;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch memset(&file->hdr, 0, sizeof(file->hdr));
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch ret = mail_transaction_log_file_read_hdr(file, &st);
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch }
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch }
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch if (ret <= 0) {
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch mail_transaction_log_file_close(file);
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch return NULL;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Bosch for (p = &log->tail; *p != NULL; p = &(*p)->next) {
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch if ((*p)->hdr.file_seq >= file->hdr.file_seq) {
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch /* log replaced with file having same sequence as
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch previous one. shouldn't happen unless previous
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch log file was corrupted.. */
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch break;
5968fa8151eecd191b1973b44dd8bec9b75810a6Phil Carmody }
5968fa8151eecd191b1973b44dd8bec9b75810a6Phil Carmody }
10962368c30afde135743fd9796122e88a708e87Stephan Bosch *p = file;
ba592dc74a004ad47dfe58edcfc1ca7297551e39Phil Carmody
10962368c30afde135743fd9796122e88a708e87Stephan Bosch return file;
5968fa8151eecd191b1973b44dd8bec9b75810a6Phil Carmody}
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Boschstatic struct mail_transaction_log_file *
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Boschmail_transaction_log_file_open_or_create(struct mail_transaction_log *log,
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch const char *path)
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch{
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch int fd;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch fd = open(path, O_RDWR);
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch if (fd == -1) {
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch if (errno != ENOENT) {
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch mail_index_file_set_syscall_error(log->index, path,
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch "open()");
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch return NULL;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch }
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch fd = mail_transaction_log_file_create(log, path, 0, 0);
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch if (fd == -1)
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch return NULL;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch }
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch return mail_transaction_log_file_fd_open(log, path, fd);
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch}
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch
10962368c30afde135743fd9796122e88a708e87Stephan Boschvoid mail_transaction_logs_clean(struct mail_transaction_log *log)
10962368c30afde135743fd9796122e88a708e87Stephan Bosch{
10962368c30afde135743fd9796122e88a708e87Stephan Bosch struct mail_transaction_log_file **p, *next;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch
baf3e87e186453fda13bd21f7cbcb2efc8492e8bTimo Sirainen for (p = &log->tail; *p != NULL; ) {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch if ((*p)->refcount != 0)
10962368c30afde135743fd9796122e88a708e87Stephan Bosch p = &(*p)->next;
7ebcb054e0d3cc4be54038cbf763ec4189d9725bStephan Bosch else {
10962368c30afde135743fd9796122e88a708e87Stephan Bosch next = (*p)->next;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch mail_transaction_log_file_close(*p);
10962368c30afde135743fd9796122e88a708e87Stephan Bosch *p = next;
10962368c30afde135743fd9796122e88a708e87Stephan Bosch }
}
if (log->tail == NULL)
log->head = NULL;
}
static int mail_transaction_log_rotate(struct mail_transaction_log *log)
{
struct mail_transaction_log_file *file;
struct stat st;
int fd, lock_type;
if (fstat(log->head->fd, &st) < 0) {
mail_index_file_set_syscall_error(log->index,
log->head->filepath,
"fstat()");
return -1;
}
fd = mail_transaction_log_file_create(log, log->head->filepath,
st.st_dev, st.st_ino);
if (fd == -1)
return -1;
file = mail_transaction_log_file_fd_open(log, log->head->filepath, fd);
if (file == NULL)
return -1;
lock_type = log->head->lock_type;
if (lock_type != F_UNLCK) {
if (mail_transaction_log_file_lock(file, lock_type) < 0) {
file->refcount--;
mail_transaction_logs_clean(log);
return -1;
}
}
if (--log->head->refcount == 0)
mail_transaction_logs_clean(log);
else
(void)mail_transaction_log_file_lock(log->head, F_UNLCK);
i_assert(log->head != file);
log->head = file;
return 0;
}
static int mail_transaction_log_recreate(struct mail_transaction_log *log)
{
unsigned int lock_id;
int ret;
if (mail_index_lock_shared(log->index, TRUE, &lock_id) < 0)
return -1;
ret = mail_transaction_log_rotate(log);
mail_index_unlock(log->index, lock_id);
return ret;
}
static int mail_transaction_log_refresh(struct mail_transaction_log *log)
{
struct mail_transaction_log_file *file;
struct stat st;
const char *path;
int ret;
path = t_strconcat(log->index->filepath,
MAIL_TRANSACTION_LOG_PREFIX, NULL);
if (stat(path, &st) < 0) {
mail_index_file_set_syscall_error(log->index, path, "stat()");
if (errno == ENOENT && log->head->lock_type == F_WRLCK) {
/* lost? */
return mail_transaction_log_recreate(log);
}
return -1;
}
if (log->head != NULL &&
log->head->st_ino == st.st_ino &&
log->head->st_dev == st.st_dev) {
/* same file */
ret = mail_transaction_log_file_read_hdr(log->head, &st);
if (ret == 0 && log->head->lock_type == F_WRLCK) {
/* corrupted, recreate */
return mail_transaction_log_recreate(log);
}
return ret <= 0 ? -1 : 0;
}
file = mail_transaction_log_file_open_or_create(log, path);
if (file == NULL)
return -1;
if (log->head != NULL) {
if (--log->head->refcount == 0)
mail_transaction_logs_clean(log);
}
log->head = file;
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;
if (file_seq > log->head->hdr.file_seq) {
if (mail_transaction_log_refresh(log) < 0)
return -1;
}
for (file = log->tail; file != NULL; file = file->next) {
if (file->hdr.file_seq == file_seq) {
*file_r = file;
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;
int ret;
i_assert(file->mmap_base == NULL);
i_assert(offset <= file->hdr.used_size);
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 = offset;
data = buffer_get_space_unsafe(file->buffer, 0, size);
ret = pread_full(file->fd, data, size, offset);
if (ret < 0 && errno == ESTALE) {
/* log file was deleted in NFS server, fail silently */
ret = 0;
}
if (ret <= 0)
return ret;
}
if (file->buffer == NULL) {
size = file->hdr.used_size - offset;
file->buffer = buffer_create_dynamic(default_pool,
size, (size_t)-1);
file->buffer_offset = offset;
size = 0;
} else {
size = buffer_get_used_size(file->buffer);
if (file->buffer_offset + size >= file->hdr.used_size) {
/* caller should have checked this.. */
return 1;
}
}
offset = file->buffer_offset + size;
size = file->hdr.used_size - file->buffer_offset - size;
if (size == 0)
return 1;
data = buffer_append_space_unsafe(file->buffer, size);
ret = pread_full(file->fd, data, size, offset);
if (ret < 0 && errno == ESTALE) {
/* log file was deleted in NFS server, fail silently */
ret = 0;
}
return ret;
}
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;
}
/* with mmap_no_write we could alternatively just write to log with
msync() rather than pwrite(). that'd cause slightly more disk I/O,
so rather use more memory. */
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 (fstat(file->fd, &st) < 0) {
mail_index_file_set_syscall_error(index, file->filepath,
"fstat()");
return -1;
}
if (st.st_size == file->hdr.used_size &&
file->buffer_offset <= start_offset && end_offset == (uoff_t)-1) {
/* we've seen the whole file.. do we have all of it mapped? */
size = file->buffer == NULL ? 0 :
buffer_get_used_size(file->buffer);
if (file->buffer_offset + size == file->hdr.used_size)
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 (mail_transaction_log_file_read_hdr(file, &st) <= 0)
return -1;
if (end_offset == (uoff_t)-1)
end_offset = file->hdr.used_size;
if (start_offset < sizeof(file->hdr)) {
mail_transaction_log_file_set_corrupted(file,
"offset (%"PRIuUOFF_T") < header size (%"PRIuSIZE_T")",
start_offset, sizeof(file->hdr));
return -1;
}
if (end_offset > file->hdr.used_size) {
mail_transaction_log_file_set_corrupted(file,
"offset (%"PRIuUOFF_T") > used_size (%u)",
end_offset, file->hdr.used_size);
return -1;
}
if (!use_mmap) {
ret = mail_transaction_log_file_read(file, start_offset);
if (ret <= 0) {
if (ret < 0) {
mail_index_file_set_syscall_error(index,
file->filepath, "pread_full()");
} else {
mail_transaction_log_file_set_corrupted(file,
"Unexpected EOF");
}
/* make sure we don't leave ourself in
inconsistent state */
if (file->buffer != NULL) {
buffer_free(file->buffer);
file->buffer = NULL;
}
}
return ret;
}
file->mmap_size = file->hdr.used_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;
}
file->buffer = buffer_create_const_data(default_pool, file->mmap_base,
file->mmap_size);
file->buffer_offset = 0;
return 1;
}
static 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, F_WRLCK) < 0)
return -1;
file->refcount++;
ret = mail_transaction_log_refresh(log);
if (--file->refcount == 0) {
mail_transaction_logs_clean(log);
file = NULL;
}
if (ret == 0 && log->head == file) {
/* success */
break;
}
if (file != NULL) {
if (mail_transaction_log_file_lock(file, F_UNLCK) < 0)
return -1;
}
if (ret < 0)
break;
/* try again */
}
return ret;
}
static int get_expunge_buf(struct mail_transaction_log *log,
struct mail_index_view *view, buffer_t *expunges)
{
struct mail_transaction_log_view *sync_view;
const struct mail_transaction_header *hdr;
const void *data;
int ret;
sync_view = mail_transaction_log_view_open(log);
ret = mail_transaction_log_view_set(sync_view, view->log_file_seq,
view->log_file_offset,
log->head->hdr.file_seq,
log->head->hdr.used_size,
MAIL_TRANSACTION_TYPE_MASK);
while ((ret = mail_transaction_log_view_next(sync_view,
&hdr, &data, NULL)) == 1) {
if ((hdr->type & MAIL_TRANSACTION_TYPE_MASK) ==
MAIL_TRANSACTION_EXPUNGE) {
mail_transaction_log_sort_expunges(expunges,
data, hdr->size);
}
}
mail_transaction_log_view_close(sync_view);
return ret;
}
static void
log_view_fix_sequences(struct mail_index_view *view, buffer_t *view_expunges,
buffer_t *buf, size_t record_size, int two, int uids)
{
// FIXME: make sure this function works correctly
const struct mail_transaction_expunge *exp, *exp_end, *exp2;
unsigned char *data;
uint32_t *seq, expunges_before, count;
size_t src_idx, dest_idx, size;
if (buf == NULL)
return;
exp = buffer_get_data(view_expunges, &size);
exp_end = exp + (size / sizeof(*exp));
if (exp == exp_end)
return;
data = buffer_get_modifyable_data(buf, &size);
expunges_before = 0;
for (src_idx = dest_idx = 0; src_idx < size; src_idx += record_size) {
seq = (uint32_t *)&data[src_idx];
while (exp != exp_end && exp->seq1 < seq[0]) {
expunges_before += exp->seq2 - exp->seq1 + 1;
exp++;
}
if (exp != exp_end && exp->seq1 == seq[0]) {
/* this sequence was expunged */
if (!two)
continue;
/* we point to next non-expunged message */
}
if (expunges_before != 0) {
if (uids) {
(void)mail_index_lookup_uid(view, seq[0],
&seq[2]);
}
seq[0] -= expunges_before;
}
if (two) {
exp2 = exp;
count = expunges_before;
while (exp2 != exp_end && exp2->seq1 <= seq[1]) {
count += exp->seq2 - exp->seq1 + 1;
exp2++;
}
if (seq[1] < count || seq[1]-count < seq[0]) {
/* whole range is expunged */
continue;
}
if (count != 0) {
if (uids) {
(void)mail_index_lookup_uid(view,
seq[1],
&seq[3]);
}
seq[1] -= count;
}
}
if (src_idx != dest_idx)
memcpy(&data[dest_idx], &data[src_idx], record_size);
dest_idx += record_size;
}
buffer_set_used_size(buf, dest_idx);
}
static int
mail_transaction_log_fix_sequences(struct mail_transaction_log *log,
struct mail_index_transaction *t)
{
buffer_t *view_expunges;
if (t->updates == NULL && t->cache_updates == NULL &&
t->expunges == NULL)
return 0;
/* all sequences are currently relative to given view. we have to
find out all the expunges since then, even the ones that aren't
yet synchronized to index file. */
view_expunges = buffer_create_dynamic(default_pool, 1024, (size_t)-1);
if (get_expunge_buf(log, t->view, view_expunges) < 0) {
buffer_free(view_expunges);
return -1;
}
log_view_fix_sequences(t->view, view_expunges, t->updates,
sizeof(struct mail_transaction_flag_update),
TRUE, FALSE);
log_view_fix_sequences(t->view, view_expunges, t->cache_updates,
sizeof(struct mail_transaction_cache_update),
FALSE, FALSE);
log_view_fix_sequences(t->view, view_expunges, t->expunges,
sizeof(struct mail_transaction_expunge),
TRUE, TRUE);
buffer_free(view_expunges);
return 0;
}
static int mail_transaction_log_fix_appends(struct mail_transaction_log *log,
struct mail_index_transaction *t)
{
struct mail_transaction_log_view *sync_view;
const struct mail_index_record *old, *old_end;
struct mail_index_record *appends, *end, *rec, *dest;
const struct mail_transaction_header *hdr;
const void *data;
size_t size;
int ret, deleted = FALSE;
if (t->appends == NULL)
return 0;
appends = buffer_get_modifyable_data(t->appends, &size);
end = PTR_OFFSET(appends, size);
if (appends == end)
return 0;
/* we'll just check that none of the appends are already in
transaction log. this could happen if we crashed before we had
a chance to update index file */
sync_view = mail_transaction_log_view_open(log);
ret = mail_transaction_log_view_set(sync_view, t->view->log_file_seq,
t->view->log_file_offset,
log->head->hdr.file_seq,
log->head->hdr.used_size,
MAIL_TRANSACTION_TYPE_MASK);
while ((ret = mail_transaction_log_view_next(sync_view,
&hdr, &data, NULL)) == 1) {
if ((hdr->type & MAIL_TRANSACTION_TYPE_MASK) !=
MAIL_TRANSACTION_APPEND)
continue;
old = data;
old_end = CONST_PTR_OFFSET(old, hdr->size);
for (; old != old_end; old++) {
/* appends are sorted */
for (rec = appends; rec != end; rec++) {
if (rec->uid >= old->uid) {
if (rec->uid == old->uid) {
rec->uid = 0;
deleted = TRUE;
}
break;
}
}
}
}
if (deleted) {
/* compress deleted appends away */
for (rec = dest = appends; rec != end; rec++) {
if (rec->uid != 0)
dest++;
else if (rec != dest)
*rec = *dest;
}
buffer_set_used_size(t->appends,
(char *)dest - (char *)appends);
}
mail_transaction_log_view_close(sync_view);
return ret;
}
static int
log_append_buffer(struct mail_transaction_log_file *file, const buffer_t *buf,
enum mail_transaction_type type, int external)
{
struct mail_transaction_header hdr;
const void *data;
size_t size;
i_assert((type & MAIL_TRANSACTION_TYPE_MASK) != 0);
if (buf != NULL) {
data = buffer_get_data(buf, &size);
if (size == 0)
return 0;
} else {
/* write only the header */
data = NULL;
size = 0;
}
hdr.type = type;
if (type == MAIL_TRANSACTION_EXPUNGE)
hdr.type |= MAIL_TRANSACTION_EXPUNGE_PROT;
if (external)
hdr.type |= MAIL_TRANSACTION_EXTERNAL;
hdr.size = size;
if (pwrite_full(file->fd, &hdr, sizeof(hdr), file->hdr.used_size) < 0)
return -1;
file->hdr.used_size += sizeof(hdr);
if (size != 0) {
if (pwrite_full(file->fd, data, size, file->hdr.used_size) < 0)
return -1;
file->hdr.used_size += size;
}
return 0;
}
int mail_transaction_log_append(struct mail_index_transaction *t,
uint32_t *log_file_seq_r,
uoff_t *log_file_offset_r)
{
struct mail_index_view *view = t->view;
struct mail_index *index;
struct mail_transaction_log *log;
struct mail_transaction_log_file *file;
size_t offset;
uoff_t append_offset;
int ret;
index = mail_index_view_get_index(view);
log = index->log;
if (t->updates == NULL && t->cache_updates == NULL &&
t->expunges == NULL && t->appends == NULL) {
/* nothing to append */
*log_file_seq_r = log->head->hdr.file_seq;
*log_file_offset_r = log->head->hdr.used_size;
return 0;
}
if (log->index->log_locked) {
i_assert(view->external);
} else {
if (mail_transaction_log_lock_head(log) < 0)
return -1;
}
if (log->head->hdr.file_seq == index->hdr->log_file_seq &&
log->head->hdr.used_size > MAIL_TRANSACTION_LOG_ROTATE_SIZE &&
log->head->last_mtime <
ioloop_time - MAIL_TRANSACTION_LOG_ROTATE_MIN_TIME) {
/* everything synced in index, we can rotate. */
if (mail_transaction_log_rotate(log) < 0) {
if (!log->index->log_locked) {
(void)mail_transaction_log_file_lock(log->head,
F_UNLCK);
}
return -1;
}
}
file = log->head;
append_offset = file->hdr.used_size;
if (mail_transaction_log_fix_sequences(log, t) < 0 ||
mail_transaction_log_fix_appends(log, t) < 0) {
if (!log->index->log_locked)
(void)mail_transaction_log_file_lock(file, F_UNLCK);
return -1;
}
ret = 0;
if (t->appends != NULL) {
ret = log_append_buffer(file, t->appends,
MAIL_TRANSACTION_APPEND,
view->external);
}
if (t->updates != NULL && ret == 0) {
ret = log_append_buffer(file, t->updates,
MAIL_TRANSACTION_FLAG_UPDATE,
view->external);
}
if (t->cache_updates != NULL && ret == 0) {
ret = log_append_buffer(file, t->cache_updates,
MAIL_TRANSACTION_CACHE_UPDATE,
view->external);
}
if (t->expunges != NULL && ret == 0) {
ret = log_append_buffer(file, t->expunges,
MAIL_TRANSACTION_EXPUNGE,
view->external);
}
if (ret == 0) {
/* rewrite used_size */
offset = offsetof(struct mail_transaction_log_header,
used_size);
ret = pwrite_full(file->fd, &file->hdr.used_size,
sizeof(file->hdr.used_size), offset);
}
if (ret == 0 && (t->updates != NULL || t->appends != NULL) &&
t->hide_transaction) {
mail_index_view_add_synced_transaction(view, file->hdr.file_seq,
append_offset);
}
if (ret < 0) {
file->hdr.used_size = append_offset;
mail_index_file_set_syscall_error(log->index, file->filepath,
"pwrite()");
} else if (fsync(file->fd) < 0) {
/* we don't know how much of it got written,
it may be corrupted now.. */
mail_index_file_set_syscall_error(log->index, file->filepath,
"fsync()");
ret = -1;
}
*log_file_seq_r = file->hdr.file_seq;
*log_file_offset_r = file->hdr.used_size;
if (!log->index->log_locked)
(void)mail_transaction_log_file_lock(file, F_UNLCK);
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;
log->index->log_locked = TRUE;
*file_seq_r = log->head->hdr.file_seq;
*file_offset_r = log->head->hdr.used_size;
return 0;
}
void mail_transaction_log_sync_unlock(struct mail_transaction_log *log)
{
i_assert(log->index->log_locked);
log->index->log_locked = FALSE;
(void)mail_transaction_log_file_lock(log->head, F_UNLCK);
}
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->hdr.used_size;
}