mail-transaction-log.c revision 73e7998716853b5b7621c06aea0022dccda70ad1
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "lib.h"
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen#include "ioloop.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "buffer.h"
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen#include "file-lock.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "file-dotlock.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "read-full.h"
abf015c9682f0f723db87a7c97bc284ef814818fTimo Sirainen#include "write-full.h"
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen#include "mmap-util.h"
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen#include "mail-index-private.h"
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen#include "mail-index-view-private.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "mail-transaction-log-private.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "mail-transaction-util.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "mail-index-transaction-private.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include <stddef.h>
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include <sys/stat.h>
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#define LOG_PREFETCH 1024
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen/* this lock should never exist for a long time.. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#define LOG_DOTLOCK_TIMEOUT 30
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#define LOG_DOTLOCK_STALE_TIMEOUT 0
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#define LOG_DOTLOCK_IMMEDIATE_STALE_TIMEOUT 300
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen#define LOG_NEW_DOTLOCK_SUFFIX ".newlock"
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainenstruct mail_transaction_add_ctx {
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen struct mail_transaction_log *log;
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen struct mail_index_view *view;
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen buffer_t *appends, *expunges;
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen buffer_t *flag_updates, *cache_updates;
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen};
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainenstatic struct mail_transaction_log_file *
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainenmail_transaction_log_file_open_or_create(struct mail_transaction_log *log,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const char *path);
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainenstatic int mail_transaction_log_rotate(struct mail_transaction_log *log,
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen int lock_type);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainenstatic int mail_transaction_log_lock_head(struct mail_transaction_log *log);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenvoid
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainenmail_transaction_log_file_set_corrupted(struct mail_transaction_log_file *file,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const char *fmt, ...)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen va_list va;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen file->hdr.indexid = 0;
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen if (pwrite_full(file->fd, &file->hdr.indexid,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen sizeof(file->hdr.indexid), 0) < 0) {
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen file->filepath, "pwrite()");
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen }
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen va_start(va, fmt);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen t_push();
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_set_error(file->log->index,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen "Corrupted transaction log file %s: %s",
2eb2cf8eeb763bd5ca9b6848dce32f0303e88ec1Timo Sirainen file->filepath, t_strdup_vprintf(fmt, va));
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen t_pop();
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen va_end(va);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainenstatic int
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenmail_transaction_log_file_dotlock(struct mail_transaction_log_file *file)
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen int ret;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (file->log->dotlock_count > 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ret = 1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen else {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ret = file_lock_dotlock(file->filepath, NULL, FALSE,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen LOG_DOTLOCK_TIMEOUT,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen LOG_DOTLOCK_STALE_TIMEOUT,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen LOG_DOTLOCK_IMMEDIATE_STALE_TIMEOUT,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen NULL, NULL, &file->log->dotlock);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ret > 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen file->log->dotlock_count++;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen file->locked = TRUE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ret < 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen file->filepath,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen "file_lock_dotlock()");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return -1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_set_error(file->log->index,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen "Timeout while waiting for release of "
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen "dotlock for transaction log file %s",
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen file->filepath);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file->log->index->index_lock_timeout = TRUE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return -1;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen}
b34fdb68d376d85b4880da4a4bdf67ae726a381bTimo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenmail_transaction_log_file_undotlock(struct mail_transaction_log_file *file)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen int ret;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (--file->log->dotlock_count > 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen ret = file_unlock_dotlock(file->filepath, &file->log->dotlock);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ret < 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen file->filepath, "file_unlock_dotlock()");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return -1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ret == 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_set_error(file->log->index,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen "Dotlock was lost for transaction log file %s",
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen file->filepath);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return -1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainenstatic int
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenmail_transaction_log_file_lock(struct mail_transaction_log_file *file)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen int ret;
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (file->locked)
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return 0;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (file->log->index->fcntl_locks_disable)
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen return mail_transaction_log_file_dotlock(file);
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen ret = file_wait_lock_full(file->fd, F_WRLCK, DEFAULT_LOCK_TIMEOUT,
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen NULL, NULL);
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen if (ret > 0) {
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen file->locked = TRUE;
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen return 0;
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen }
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen if (ret < 0) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen file->filepath,
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen "file_wait_lock()");
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen return -1;
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen }
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen mail_index_set_error(file->log->index,
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen "Timeout while waiting for release of "
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen "fcntl() lock for transaction log file %s",
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen file->filepath);
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen file->log->index->index_lock_timeout = TRUE;
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen return -1;
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen}
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainenstatic void
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainenmail_transaction_log_file_unlock(struct mail_transaction_log_file *file)
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen{
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen int ret;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (!file->locked)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen file->locked = FALSE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (file->log->index->fcntl_locks_disable) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_transaction_log_file_undotlock(file);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen ret = file_wait_lock(file->fd, F_UNLCK);
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (ret <= 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen file->filepath,
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen "file_wait_lock()");
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen#define INDEX_HAS_MISSING_LOGS(index, file) \
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen !(((file)->hdr.file_seq == (index)->hdr->log_file_seq && \
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen (index)->hdr->log_file_offset >= \
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen sizeof(struct mail_transaction_log_header)) || \
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen ((file)->hdr.prev_file_seq == (index)->hdr->log_file_seq && \
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen (file)->hdr.prev_file_offset == (index)->hdr->log_file_offset))
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int mail_transaction_log_check_file_seq(struct mail_transaction_log *log)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen{
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen struct mail_index *index = log->index;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen struct mail_transaction_log_file *file;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen unsigned int lock_id;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen int ret;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (mail_transaction_log_lock_head(log) < 0)
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen return -1;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen file = log->head;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen file->refcount++;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ret = mail_index_lock_shared(index, TRUE, &lock_id);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ret == 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ret = mail_index_map(index, FALSE);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ret <= 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ret = -1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen else if (INDEX_HAS_MISSING_LOGS(index, file)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* broken - fix it by creating a new log file */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ret = mail_transaction_log_rotate(log, F_UNLCK);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (--file->refcount == 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_transaction_logs_clean(log);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen else
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_transaction_log_file_unlock(file);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return ret;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstruct mail_transaction_log *
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenmail_transaction_log_open_or_create(struct mail_index *index)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct mail_transaction_log *log;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const char *path;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen log = i_new(struct mail_transaction_log, 1);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen log->index = index;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen path = t_strconcat(log->index->filepath,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen MAIL_TRANSACTION_LOG_PREFIX, NULL);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen log->head = mail_transaction_log_file_open_or_create(log, path);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (log->head == NULL) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_free(log);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return NULL;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (index->fd != -1 &&
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen INDEX_HAS_MISSING_LOGS(index, log->head)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* head log file isn't same as head index file -
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen shouldn't happen except in race conditions. lock them and
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen check again - FIXME: missing error handling. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen (void)mail_transaction_log_check_file_seq(log);
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen }
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen return log;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenvoid mail_transaction_log_close(struct mail_transaction_log *log)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_transaction_log_views_close(log);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen log->head->refcount--;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_transaction_logs_clean(log);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen log->index->log = NULL;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_free(log);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenmail_transaction_log_file_close(struct mail_transaction_log_file *file)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_transaction_log_file_unlock(file);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (file->buffer != NULL)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen buffer_free(file->buffer);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (file->mmap_base != NULL) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (munmap(file->mmap_base, file->mmap_size) < 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen file->filepath,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen "munmap()");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (close(file->fd) < 0) {
9aceb071780a949f4e8bf41d3cf80735d9ac7fdfTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
9aceb071780a949f4e8bf41d3cf80735d9ac7fdfTimo Sirainen file->filepath, "close()");
9aceb071780a949f4e8bf41d3cf80735d9ac7fdfTimo Sirainen }
9aceb071780a949f4e8bf41d3cf80735d9ac7fdfTimo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_free(file->filepath);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_free(file);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenmail_transaction_log_file_read_hdr(struct mail_transaction_log_file *file)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen int ret;
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ret = pread_full(file->fd, &file->hdr, sizeof(file->hdr), 0);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ret < 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen // FIXME: handle ESTALE
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen file->filepath,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen "pread_full()");
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen return -1;
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen }
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen if (ret == 0) {
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen mail_transaction_log_file_set_corrupted(file,
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen "unexpected end of file while reading header");
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen return 0;
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen }
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen if (file->hdr.indexid == 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* corrupted */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_set_error(file->log->index,
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen "Transaction log file %s: marked corrupted",
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen file->filepath);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen return 0;
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (file->hdr.indexid != file->log->index->indexid) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (file->log->index->fd == -1) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* creating index file, silently rebuild
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen transaction log as well */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen /* index file was probably just rebuilt and we don't know
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen about it yet */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_set_error(file->log->index,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen "Transaction log file %s: invalid indexid (%u != %u)",
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen file->filepath, file->hdr.indexid,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen file->log->index->indexid);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 0;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen }
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen return 1;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen}
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainenstatic int
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainenmail_transaction_log_file_create2(struct mail_transaction_log *log,
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen const char *path, int fd,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen dev_t dev, ino_t ino)
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen{
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk struct mail_index *index = log->index;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen struct mail_transaction_log_header hdr;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen struct stat st;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen unsigned int lock_id;
263946a65b625fd4198619a8626db0f36bbafd66Timo Sirainen int fd2, ret;
263946a65b625fd4198619a8626db0f36bbafd66Timo Sirainen
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen /* log creation is locked now - see if someone already created it */
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen fd2 = open(path, O_RDWR);
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen if (fd2 != -1) {
02a6291366caff79793db35d479e2a062bec2af4Timo Sirainen if ((ret = fstat(fd2, &st)) < 0) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen mail_index_file_set_syscall_error(index, path,
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen "fstat()");
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen } else if (st.st_ino == ino && CMP_DEV_T(st.st_dev, dev)) {
573085b4b25b0bbae8d27969df2c91702eefa23eTimo Sirainen /* same file, still broken */
0ee3fdb5e94ae6f34cb873ca3c9858342621e55fTimo Sirainen } else {
0ee3fdb5e94ae6f34cb873ca3c9858342621e55fTimo Sirainen (void)file_dotlock_delete(path, LOG_NEW_DOTLOCK_SUFFIX,
0ee3fdb5e94ae6f34cb873ca3c9858342621e55fTimo Sirainen fd);
0ee3fdb5e94ae6f34cb873ca3c9858342621e55fTimo Sirainen return fd2;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
e5acc283bf030b0b5c79ca4e52d315c516a299faPascal Volk (void)close(fd2);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen fd2 = -1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ret < 0)
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen return -1;
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen } else if (errno != ENOENT) {
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen mail_index_file_set_syscall_error(index, path, "open()");
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen return -1;
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen memset(&hdr, 0, sizeof(hdr));
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen hdr.indexid = index->indexid;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (index->fd != -1) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (mail_index_lock_shared(index, TRUE, &lock_id) < 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return -1;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen hdr.prev_file_seq = index->hdr->log_file_seq;
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen hdr.prev_file_offset = index->hdr->log_file_offset;
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen }
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen hdr.file_seq = index->hdr->log_file_seq+1;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen if (index->fd != -1)
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen mail_index_unlock(index, lock_id);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen if (log->head != NULL && hdr.file_seq <= log->head->hdr.file_seq) {
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen /* make sure the sequence grows */
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen hdr.file_seq = log->head->hdr.file_seq+1;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen }
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen if (write_full(fd, &hdr, sizeof(hdr)) < 0) {
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen mail_index_file_set_syscall_error(index, path,
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen "write_full()");
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen return -1;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen }
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen fd2 = dup(fd);
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen if (fd2 < 0) {
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen mail_index_file_set_syscall_error(index, path, "dup()");
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen return -1;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen }
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen if (file_dotlock_replace(path, LOG_NEW_DOTLOCK_SUFFIX, fd, FALSE) <= 0)
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen return -1;
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen /* success */
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen return fd2;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen}
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainenstatic int
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainenmail_transaction_log_file_create(struct mail_transaction_log *log,
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen const char *path, dev_t dev, ino_t ino)
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen{
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen int fd, fd2;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen /* With dotlocking we might already have path.lock created, so this
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen filename has to be different. */
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen fd = file_dotlock_open(path, NULL, LOG_NEW_DOTLOCK_SUFFIX,
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen LOG_DOTLOCK_TIMEOUT,
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen LOG_DOTLOCK_STALE_TIMEOUT,
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen LOG_DOTLOCK_IMMEDIATE_STALE_TIMEOUT, NULL, NULL);
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen if (fd == -1) {
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen mail_index_file_set_syscall_error(log->index, path,
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen "file_dotlock_open()");
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen return -1;
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen }
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen fd2 = mail_transaction_log_file_create2(log, path, fd, dev, ino);
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen if (fd2 < 0) {
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen (void)file_dotlock_delete(path, LOG_NEW_DOTLOCK_SUFFIX, fd);
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen return -1;
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen }
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen return fd2;
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen}
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainenstatic struct mail_transaction_log_file *
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainenmail_transaction_log_file_fd_open(struct mail_transaction_log *log,
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen const char *path, int fd)
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen{
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen struct mail_transaction_log_file **p;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen struct mail_transaction_log_file *file;
e0740628f6ca05f4bc79a9d8a90b650f4d38d4d0Timo Sirainen struct stat st;
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen int ret;
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen if (fstat(fd, &st) < 0) {
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen mail_index_file_set_syscall_error(log->index, path, "fstat()");
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen (void)close(fd);
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen return NULL;
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen }
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen file = i_new(struct mail_transaction_log_file, 1);
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen file->refcount = 1;
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen file->log = log;
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen file->filepath = i_strdup(path);
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen file->fd = fd;
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen file->st_dev = st.st_dev;
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen file->st_ino = st.st_ino;
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen file->last_mtime = st.st_mtime;
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen file->sync_offset = sizeof(struct mail_transaction_log_header);
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen ret = mail_transaction_log_file_read_hdr(file);
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen if (ret == 0) {
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen /* corrupted header */
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen fd = mail_transaction_log_file_create(log, path,
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen st.st_dev, st.st_ino);
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen if (fd == -1)
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen ret = -1;
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen else if (fstat(fd, &st) < 0) {
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen mail_index_file_set_syscall_error(log->index, path,
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen "fstat()");
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen (void)close(fd);
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen fd = -1;
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen ret = -1;
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen }
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen if (fd != -1) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen (void)close(file->fd);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file->fd = fd;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file->st_dev = st.st_dev;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file->st_ino = st.st_ino;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen file->last_mtime = st.st_mtime;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen memset(&file->hdr, 0, sizeof(file->hdr));
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ret = mail_transaction_log_file_read_hdr(file);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen }
c3a636e4c9ae776e0eed06b6d7ad1ccfb6003afdTimo Sirainen if (ret <= 0) {
f934b271c69c7b3e5e3bca23ff9b3ab6187262c2Timo Sirainen mail_transaction_log_file_close(file);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return NULL;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen if (log->index->map != NULL &&
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file->hdr.file_seq == log->index->map->log_file_seq &&
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen log->index->map->log_file_offset != 0) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen /* we can get a valid log offset from index file. initialize
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen sync_offset from it so we don't have to read the whole log
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file from beginning. */
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file->sync_offset = log->index->map->log_file_offset;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen for (p = &log->tail; *p != NULL; p = &(*p)->next) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if ((*p)->hdr.file_seq >= file->hdr.file_seq) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen /* log replaced with file having same sequence as
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen previous one. shouldn't happen unless previous
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen log file was corrupted.. */
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen break;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
8f5b34c22e4c3bfb35ca13c4744867eb5ddbd3d6Timo Sirainen }
8f5b34c22e4c3bfb35ca13c4744867eb5ddbd3d6Timo Sirainen *p = file;
8f5b34c22e4c3bfb35ca13c4744867eb5ddbd3d6Timo Sirainen
8f5b34c22e4c3bfb35ca13c4744867eb5ddbd3d6Timo Sirainen return file;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic struct mail_transaction_log_file *
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenmail_transaction_log_file_open_or_create(struct mail_transaction_log *log,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen const char *path)
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen{
2eb0402a28bd0422e0170160808c67d6c7274689Timo Sirainen int fd;
97180ea9c26c4de0807daaad21e03c80643b09fdTimo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen fd = open(path, O_RDWR);
2eb0402a28bd0422e0170160808c67d6c7274689Timo Sirainen if (fd == -1) {
2eb0402a28bd0422e0170160808c67d6c7274689Timo Sirainen if (errno != ENOENT) {
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen mail_index_file_set_syscall_error(log->index, path,
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen "open()");
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen return NULL;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen }
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen fd = mail_transaction_log_file_create(log, path, 0, 0);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen if (fd == -1)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen return NULL;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen }
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen return mail_transaction_log_file_fd_open(log, path, fd);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen}
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainenvoid mail_transaction_logs_clean(struct mail_transaction_log *log)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen{
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen struct mail_transaction_log_file **p, *next;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen for (p = &log->tail; *p != NULL; ) {
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen if ((*p)->refcount != 0)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen p = &(*p)->next;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen else {
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen next = (*p)->next;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen mail_transaction_log_file_close(*p);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen *p = next;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen }
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen }
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen if (log->tail == NULL)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen log->head = NULL;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen}
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainenstatic int
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainenmail_transaction_log_rotate(struct mail_transaction_log *log, int lock)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen{
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen struct mail_transaction_log_file *file;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen struct stat st;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen int fd;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen if (fstat(log->head->fd, &st) < 0) {
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen mail_index_file_set_syscall_error(log->index,
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen log->head->filepath,
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen "fstat()");
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen return -1;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen }
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen fd = mail_transaction_log_file_create(log, log->head->filepath,
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen st.st_dev, st.st_ino);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen if (fd == -1)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen return -1;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen file = mail_transaction_log_file_fd_open(log, log->head->filepath, fd);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen if (file == NULL)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen return -1;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen if (lock) {
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen if (mail_transaction_log_file_lock(file) < 0) {
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen file->refcount--;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen mail_transaction_logs_clean(log);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen return -1;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen }
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen }
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen i_assert(file->locked == lock);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen if (--log->head->refcount == 0)
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen mail_transaction_logs_clean(log);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen else
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen mail_transaction_log_file_unlock(log->head);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen i_assert(log->head != file);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen log->head = file;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen return 0;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen}
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainenstatic int mail_transaction_log_recreate(struct mail_transaction_log *log)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen unsigned int lock_id;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen int ret;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (mail_index_lock_shared(log->index, TRUE, &lock_id) < 0)
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return -1;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen ret = mail_transaction_log_rotate(log, FALSE);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen mail_index_unlock(log->index, lock_id);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen return ret;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen}
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainenstatic int mail_transaction_log_refresh(struct mail_transaction_log *log)
7744586e3e0fd60158abfbb03a233d3bd8d6c48bTimo Sirainen{
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen struct mail_transaction_log_file *file;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen struct stat st;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen const char *path;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen path = t_strconcat(log->index->filepath,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen MAIL_TRANSACTION_LOG_PREFIX, NULL);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (stat(path, &st) < 0) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen mail_index_file_set_syscall_error(log->index, path, "stat()");
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (errno == ENOENT && log->head->locked) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen /* lost? */
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return mail_transaction_log_recreate(log);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return -1;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen if (log->head != NULL &&
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen log->head->st_ino == st.st_ino &&
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen CMP_DEV_T(log->head->st_dev, st.st_dev)) {
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen /* same file */
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen return 0;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen }
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen file = mail_transaction_log_file_open_or_create(log, path);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (file == NULL)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return -1;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen i_assert(!file->locked);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (log->head != NULL) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (--log->head->refcount == 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_transaction_logs_clean(log);
f934b271c69c7b3e5e3bca23ff9b3ab6187262c2Timo Sirainen }
97180ea9c26c4de0807daaad21e03c80643b09fdTimo Sirainen
97180ea9c26c4de0807daaad21e03c80643b09fdTimo Sirainen log->head = file;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen return 0;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen}
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenint mail_transaction_log_file_find(struct mail_transaction_log *log,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen uint32_t file_seq,
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen struct mail_transaction_log_file **file_r)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen struct mail_transaction_log_file *file;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen if (file_seq > log->head->hdr.file_seq) {
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen if (mail_transaction_log_refresh(log) < 0)
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen return -1;
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen }
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen for (file = log->tail; file != NULL; file = file->next) {
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen if (file->hdr.file_seq == file_seq) {
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen *file_r = file;
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen return 1;
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen }
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen }
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen return 0;
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen}
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainenstatic int
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainenmail_transaction_log_file_sync(struct mail_transaction_log_file *file)
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen{
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen const struct mail_transaction_header *hdr;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen const void *data;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen size_t size;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen uint32_t hdr_size;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen data = buffer_get_data(file->buffer, &size);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen while (file->sync_offset - file->buffer_offset + sizeof(*hdr) <= size) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen hdr = CONST_PTR_OFFSET(data, file->sync_offset -
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen file->buffer_offset);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen hdr_size = mail_index_offset_to_uint32(hdr->size);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (hdr_size == 0) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen /* unfinished */
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen if (file->mmap_base == NULL) {
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen size = file->sync_offset - file->buffer_offset;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen buffer_set_used_size(file->buffer, size);
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen }
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen return 0;
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (hdr_size < sizeof(*hdr)) {
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen mail_transaction_log_file_set_corrupted(file,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen "hdr.size too small (%u)", hdr_size);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return -1;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen if (file->sync_offset - file->buffer_offset + hdr_size > size)
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen break;
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen file->sync_offset += hdr_size;
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return 0;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen}
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainenstatic int
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainenmail_transaction_log_file_read(struct mail_transaction_log_file *file,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen uoff_t offset)
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen{
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen void *data;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen size_t size;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen uint32_t read_offset;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen int ret;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen i_assert(file->mmap_base == NULL);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (file->buffer != NULL && file->buffer_offset > offset) {
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen /* we have to insert missing data to beginning of buffer */
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen size = file->buffer_offset - offset;
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen buffer_copy(file->buffer, size, file->buffer, 0, (size_t)-1);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file->buffer_offset -= size;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen data = buffer_get_space_unsafe(file->buffer, 0, size);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen ret = pread_full(file->fd, data, size, offset);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (ret == 0) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen mail_transaction_log_file_set_corrupted(file,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen "Unexpected end of file");
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen return 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen if (ret < 0) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (errno == ESTALE) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen /* log file was deleted in NFS server,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen fail silently */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 0;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen mail_index_file_set_syscall_error(file->log->index,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file->filepath,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen "pread()");
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return -1;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen if (file->buffer == NULL) {
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen file->buffer = buffer_create_dynamic(default_pool,
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen LOG_PREFETCH, (size_t)-1);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen file->buffer_offset = offset;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen /* read all records */
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen read_offset = file->buffer_offset + buffer_get_used_size(file->buffer);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen do {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen data = buffer_append_space_unsafe(file->buffer, LOG_PREFETCH);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen ret = pread(file->fd, data, LOG_PREFETCH, read_offset);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen if (ret > 0)
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen read_offset += ret;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen size = read_offset - file->buffer_offset;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen buffer_set_used_size(file->buffer, size);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen if (mail_transaction_log_file_sync(file) < 0)
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen return -1;
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen } while (ret > 0 || (ret < 0 && errno == EINTR));
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen if (ret == 0) {
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen /* EOF */
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen buffer_set_used_size(file->buffer,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file->sync_offset - file->buffer_offset);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return 1;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (errno == ESTALE) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen /* log file was deleted in NFS server, fail silently */
8f5b34c22e4c3bfb35ca13c4744867eb5ddbd3d6Timo Sirainen buffer_set_used_size(file->buffer,
8f5b34c22e4c3bfb35ca13c4744867eb5ddbd3d6Timo Sirainen offset - file->buffer_offset);
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen return 0;
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen }
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen mail_index_file_set_syscall_error(file->log->index, file->filepath,
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen "pread()");
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return -1;
95d9395d15540b3a96f75c7f9fd73e6d8ad5e897Timo Sirainen}
95d9395d15540b3a96f75c7f9fd73e6d8ad5e897Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainenint mail_transaction_log_file_map(struct mail_transaction_log_file *file,
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen uoff_t start_offset, uoff_t end_offset)
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen{
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen struct mail_index *index = file->log->index;
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen size_t size;
48ada47cce07fb7195a3437224c7c25f542326b0Timo Sirainen struct stat st;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen int ret, use_mmap;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_assert(start_offset <= end_offset);
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen if (file->hdr.indexid == 0) {
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen /* corrupted */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 0;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (start_offset < sizeof(file->hdr)) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen mail_transaction_log_file_set_corrupted(file,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen "offset (%"PRIuUOFF_T") < header size (%"PRIuSIZE_T")",
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen start_offset, sizeof(file->hdr));
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return -1;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen /* with mmap_no_write we could alternatively just write to log with
f38485358ffc04c3466b917770575e29deef24c3Timo Sirainen msync() rather than pwrite(). that'd cause slightly more disk I/O,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen so rather use more memory. */
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen use_mmap = !index->mmap_disable && !index->mmap_no_write;
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (file->buffer != NULL && file->buffer_offset <= start_offset) {
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen /* see if we already have it */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen size = buffer_get_used_size(file->buffer);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (file->buffer_offset + size >= end_offset)
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return 1;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (file->mmap_base != NULL || use_mmap) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (fstat(file->fd, &st) < 0) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen mail_index_file_set_syscall_error(index, file->filepath,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen "fstat()");
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return -1;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (file->mmap_base != NULL && st.st_size == file->mmap_size &&
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file->buffer_offset <= start_offset) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen /* it's all mmaped already */
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen i_assert(end_offset == (uoff_t)-1);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return 1;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (file->buffer != NULL &&
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen (file->mmap_base != NULL || use_mmap)) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen buffer_free(file->buffer);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file->buffer = NULL;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (file->mmap_base != NULL) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen if (munmap(file->mmap_base, file->mmap_size) < 0) {
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen mail_index_file_set_syscall_error(index, file->filepath,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen "munmap()");
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file->mmap_base = NULL;
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen }
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen if (!use_mmap) {
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen ret = mail_transaction_log_file_read(file, start_offset);
97180ea9c26c4de0807daaad21e03c80643b09fdTimo Sirainen if (ret <= 0) {
97180ea9c26c4de0807daaad21e03c80643b09fdTimo Sirainen /* make sure we don't leave ourself in
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen inconsistent state */
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen if (file->buffer != NULL) {
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen buffer_free(file->buffer);
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen file->buffer = NULL;
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen }
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen return ret;
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen }
97180ea9c26c4de0807daaad21e03c80643b09fdTimo Sirainen } else {
97180ea9c26c4de0807daaad21e03c80643b09fdTimo Sirainen file->mmap_size = st.st_size;
97180ea9c26c4de0807daaad21e03c80643b09fdTimo Sirainen file->mmap_base = mmap(NULL, file->mmap_size, PROT_READ,
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen MAP_SHARED, file->fd, 0);
97180ea9c26c4de0807daaad21e03c80643b09fdTimo Sirainen if (file->mmap_base == MAP_FAILED) {
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen file->mmap_base = NULL;
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen mail_index_file_set_syscall_error(index, file->filepath,
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen "mmap()");
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen return -1;
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen }
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen if (file->mmap_size > mmap_get_page_size()) {
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen if (madvise(file->mmap_base, file->mmap_size,
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen MADV_SEQUENTIAL) < 0) {
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen mail_index_file_set_syscall_error(index,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file->filepath, "madvise()");
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen }
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen }
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen
97180ea9c26c4de0807daaad21e03c80643b09fdTimo Sirainen file->buffer = buffer_create_const_data(default_pool,
88b315f36ba082708ab6f53ea1924c54d9be0aefTimo Sirainen file->mmap_base,
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen file->mmap_size);
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen file->buffer_offset = 0;
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen if (mail_transaction_log_file_sync(file) < 0)
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen return -1;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen }
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen if (end_offset != (uoff_t)-1 && end_offset > file->sync_offset) {
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen mail_transaction_log_file_set_corrupted(file,
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen "end_offset (%"PRIuUOFF_T") > current sync_offset "
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen "(%"PRIuSIZE_T")", end_offset, file->sync_offset);
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen return -1;
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen }
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen return 1;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainenstatic int mail_transaction_log_lock_head(struct mail_transaction_log *log)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct mail_transaction_log_file *file;
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen int ret = 0;
c99fe55d4535d839a6ad0735c4719e076a1adb2cTimo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen /* we want to get the head file locked. this is a bit racy,
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen since by the time we have it locked a new log file may have been
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen created.
fa2433aebcf3fccfa30ca9eed9b1a9166cf92ee2Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen creating new log file requires locking the head file, so if we
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen can lock it and don't see another file, we can be sure no-one is
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen creating a new log at the moment */
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen for (;;) {
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen file = log->head;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (mail_transaction_log_file_lock(file) < 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return -1;
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen file->refcount++;
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen ret = mail_transaction_log_refresh(log);
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen if (--file->refcount == 0) {
47e9fdee55c2074425cf0316f4f64fbbb790301cTimo Sirainen mail_transaction_logs_clean(log);
7e95ba7f38b9b421287d36c6152f8a9e6b9f225bTimo Sirainen file = NULL;
7e95ba7f38b9b421287d36c6152f8a9e6b9f225bTimo Sirainen }
7e95ba7f38b9b421287d36c6152f8a9e6b9f225bTimo Sirainen
7e95ba7f38b9b421287d36c6152f8a9e6b9f225bTimo Sirainen if (ret == 0 && log->head == file) {
7e95ba7f38b9b421287d36c6152f8a9e6b9f225bTimo Sirainen /* success */
7e95ba7f38b9b421287d36c6152f8a9e6b9f225bTimo Sirainen break;
608bdb7f008cd5cd332d727018a9e8173abec998Timo Sirainen }
608bdb7f008cd5cd332d727018a9e8173abec998Timo Sirainen
608bdb7f008cd5cd332d727018a9e8173abec998Timo Sirainen if (file != NULL)
608bdb7f008cd5cd332d727018a9e8173abec998Timo Sirainen mail_transaction_log_file_unlock(file);
608bdb7f008cd5cd332d727018a9e8173abec998Timo Sirainen
608bdb7f008cd5cd332d727018a9e8173abec998Timo Sirainen if (ret < 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen break;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
2eb0402a28bd0422e0170160808c67d6c7274689Timo Sirainen /* try again */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
2eb0402a28bd0422e0170160808c67d6c7274689Timo Sirainen
2eb0402a28bd0422e0170160808c67d6c7274689Timo Sirainen return ret;
2eb0402a28bd0422e0170160808c67d6c7274689Timo Sirainen}
2eb0402a28bd0422e0170160808c67d6c7274689Timo Sirainen
2eb0402a28bd0422e0170160808c67d6c7274689Timo Sirainenstatic void
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenmail_transaction_log_append_fix(struct mail_index_transaction *t,
const struct mail_transaction_header *hdr,
const void *data)
{
const struct mail_index_record *old, *old_end;
struct mail_index_record *appends, *end, *rec, *dest;
size_t size;
int deleted = FALSE;
if (t->appends == NULL)
return;
appends = buffer_get_modifyable_data(t->appends, &size);
end = PTR_OFFSET(appends, size);
if (appends == end)
return;
/* 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 */
old_end = CONST_PTR_OFFSET(data, hdr->size);
for (old = data; 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);
}
}
static void
transaction_save_extra_intro(struct mail_index_transaction *t,
const struct mail_transaction_extra_intro *intro)
{
const char *name;
void *p;
uint32_t data_id;
size_t pos;
if (t->extra_intros == NULL) {
t->extra_intros =
buffer_create_dynamic(default_pool, 128, (size_t)-1);
}
t_push();
name = t_strndup((const char *)(intro+1), intro->name_size);
data_id = mail_index_register_record_extra(t->view->index, name,
intro->hdr_size,
intro->record_size);
pos = data_id * sizeof(intro->data_id);
if (pos > t->extra_intros->used) {
/* unused records are -1 */
p = buffer_append_space_unsafe(t->extra_intros,
pos - t->extra_intros->used);
memset(p, 0xff, pos - t->extra_intros->used);
}
buffer_write(t->extra_intros, pos,
&intro->data_id, sizeof(intro->data_id));
if (intro->data_id > t->extra_intros_max_id)
t->extra_intros_max_id = intro->data_id;
t_pop();
}
static int mail_transaction_log_scan_pending(struct mail_transaction_log *log,
struct mail_index_transaction *t)
{
struct mail_transaction_log_view *sync_view;
const struct mail_transaction_header *hdr;
const void *data;
uint32_t max_cache_file_seq = 0;
int ret;
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, (uoff_t)-1,
MAIL_TRANSACTION_TYPE_MASK);
while ((ret = mail_transaction_log_view_next(sync_view,
&hdr, &data, NULL)) == 1) {
switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
case MAIL_TRANSACTION_APPEND:
mail_transaction_log_append_fix(t, hdr, data);
break;
case MAIL_TRANSACTION_CACHE_RESET: {
const struct mail_transaction_cache_reset *reset = data;
max_cache_file_seq = reset->new_file_seq;
break;
}
case MAIL_TRANSACTION_EXTRA_INTRO: {
const struct mail_transaction_extra_intro *intro = data;
transaction_save_extra_intro(t, intro);
break;
}
}
}
/* make sure we're not writing cache_offsets to old cache file */
if (t->new_cache_file_seq == 0 && max_cache_file_seq != 0 &&
max_cache_file_seq != t->last_cache_file_seq &&
t->cache_updates != NULL) {
buffer_free(t->cache_updates);
t->cache_updates = NULL;
if (t->appends != NULL) {
struct mail_index_record *rec;
size_t i, size;
rec = buffer_get_modifyable_data(t->appends, &size);
size /= sizeof(*rec);
for (i = 0; i < size; i++)
rec[i].cache_offset = 0;
}
}
mail_transaction_log_view_close(sync_view);
return ret;
}
static int log_append_buffer(struct mail_transaction_log_file *file,
const buffer_t *buf, const buffer_t *hdr_buf,
enum mail_transaction_type type, int external)
{
struct mail_transaction_header hdr;
const void *data, *hdr_data;
size_t size, hdr_data_size;
uint32_t hdr_size;
i_assert((type & MAIL_TRANSACTION_TYPE_MASK) != 0);
data = buffer_get_data(buf, &size);
if (size == 0)
return 0;
i_assert((size % 4) == 0);
if (hdr_buf != NULL) {
hdr_data = buffer_get_data(hdr_buf, &hdr_data_size);
i_assert((hdr_data_size % 4) == 0);
} else {
hdr_data = NULL;
hdr_data_size = 0;
}
memset(&hdr, 0, sizeof(hdr));
hdr.type = type;
if (type == MAIL_TRANSACTION_EXPUNGE)
hdr.type |= MAIL_TRANSACTION_EXPUNGE_PROT;
if (external)
hdr.type |= MAIL_TRANSACTION_EXTERNAL;
hdr_size =
mail_index_uint32_to_offset(sizeof(hdr) + size + hdr_data_size);
if (file->first_append_size == 0) {
/* size will be written later once everything is in disk */
file->first_append_size = hdr_size;
} else {
hdr.size = hdr_size;
}
if (pwrite_full(file->fd, &hdr, sizeof(hdr), file->sync_offset) < 0)
return -1;
file->sync_offset += sizeof(hdr);
if (hdr_data_size > 0) {
if (pwrite_full(file->fd, hdr_data, hdr_data_size,
file->sync_offset) < 0)
return -1;
file->sync_offset += hdr_data_size;
}
if (pwrite_full(file->fd, data, size, file->sync_offset) < 0)
return -1;
file->sync_offset += size;
return 0;
}
static const buffer_t *get_cache_reset_buf(struct mail_index_transaction *t)
{
struct mail_transaction_cache_reset u;
buffer_t *buf;
memset(&u, 0, sizeof(u));
u.new_file_seq = t->new_cache_file_seq;
buf = buffer_create_static(pool_datastack_create(), sizeof(u));
buffer_append(buf, &u, sizeof(u));
return buf;
}
static const buffer_t *
log_get_hdr_update_buffer(struct mail_index_transaction *t)
{
buffer_t *buf;
struct mail_transaction_header_update u;
uint16_t offset;
int state = 0;
memset(&u, 0, sizeof(u));
buf = buffer_create_dynamic(pool_datastack_create(), 256, (size_t)-1);
for (offset = 0; offset <= sizeof(t->hdr_change); offset++) {
if (offset < sizeof(t->hdr_change) && t->hdr_mask[offset]) {
if (state == 0) {
u.offset = offset;
state++;
}
} else {
if (state > 0) {
u.size = offset - u.offset;
buffer_append(buf, &u, sizeof(uint16_t)*2);
buffer_append(buf, t->hdr_change + u.offset,
u.size);
state = 0;
}
}
}
return buf;
}
static int
mail_transaction_log_register_extra(struct mail_transaction_log_file *file,
struct mail_index_transaction *t,
uint32_t data_id, uint32_t *idx_r)
{
const struct mail_index_extra_record_info *einfo;
struct mail_transaction_extra_intro *intro;
const uint32_t *id_map;
buffer_t *buf;
size_t size;
int ret;
/* first check if it's already in nonsynced part of transaction log */
if (t->extra_intros != NULL) {
id_map = buffer_get_data(t->extra_intros, &size);
size /= sizeof(*id_map);
if (data_id < size && id_map[data_id] != (uint32_t)-1) {
*idx_r = id_map[data_id];
return 0;
}
}
*idx_r = t->extra_intros_max_id++;
einfo = t->view->index->extra_infos->data;
einfo += data_id;
/* nope, register */
t_push();
buf = buffer_create_dynamic(pool_datastack_create(), 128, (size_t)-1);
intro = buffer_append_space_unsafe(buf, sizeof(*intro));
intro->data_id = *idx_r;
intro->hdr_size = einfo->hdr_size;
intro->record_size = einfo->record_size;
intro->name_size = strlen(einfo->name);
buffer_append(buf, einfo->name, intro->name_size);
if ((buf->used % 4) != 0)
buffer_append(buf, null4, 4 - (buf->used % 4));
ret = log_append_buffer(file, buf, NULL, MAIL_TRANSACTION_EXTRA_INTRO,
t->view->external);
t_pop();
return ret;
}
int mail_transaction_log_append(struct mail_index_transaction *t,
uint32_t *log_file_seq_r,
uoff_t *log_file_offset_r)
{
struct mail_transaction_extra_rec_header extra_rec_hdr;
struct mail_index_view *view = t->view;
struct mail_index *index;
struct mail_transaction_log *log;
struct mail_transaction_log_file *file;
struct mail_index_header idx_hdr;
uoff_t append_offset;
buffer_t *hdr_buf, **updates;
unsigned int i, lock_id;
uint32_t idx;
size_t size;
int ret;
index = mail_index_view_get_index(view);
log = index->log;
if (!t->log_updates) {
/* nothing to append */
*log_file_seq_r = 0;
*log_file_offset_r = 0;
return 0;
}
if (log->index->log_locked) {
i_assert(view->external);
} else {
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;
}
}
if (mail_index_lock_shared(log->index, TRUE, &lock_id) < 0) {
if (!log->index->log_locked)
mail_transaction_log_file_unlock(log->head);
return -1;
}
idx_hdr = *log->index->hdr;
mail_index_unlock(log->index, lock_id);
if (log->head->sync_offset > MAIL_TRANSACTION_LOG_ROTATE_SIZE &&
log->head->last_mtime <
ioloop_time - MAIL_TRANSACTION_LOG_ROTATE_MIN_TIME) {
/* we might want to rotate, but check first that everything is
synced in index. */
if (log->head->hdr.file_seq == idx_hdr.log_file_seq &&
log->head->sync_offset == idx_hdr.log_file_offset) {
if (mail_transaction_log_rotate(log, TRUE) < 0) {
/* that didn't work. well, try to continue
anyway */
}
}
}
file = log->head;
file->first_append_size = 0;
append_offset = file->sync_offset;
if (t->cache_updates != NULL &&
t->last_cache_file_seq < idx_hdr.cache_file_seq) {
/* cache_offsets point to old file, don't allow */
buffer_free(t->cache_updates);
t->cache_updates = NULL;
}
t->extra_intros_max_id = t->view->index->map->extra_infos == NULL ? 0 :
(t->view->index->map->extra_infos->used /
sizeof(struct mail_index_extra_record_info));
if (t->appends != NULL ||
(t->cache_updates != NULL && t->new_cache_file_seq == 0) ||
(t->extra_rec_updates != NULL && t->extra_rec_updates->used > 0)) {
if (mail_transaction_log_scan_pending(log, t) < 0) {
if (!log->index->log_locked)
mail_transaction_log_file_unlock(file);
return -1;
}
}
ret = 0;
if (t->appends != NULL) {
ret = log_append_buffer(file, t->appends, NULL,
MAIL_TRANSACTION_APPEND,
view->external);
}
if (t->updates != NULL && ret == 0) {
ret = log_append_buffer(file, t->updates, NULL,
MAIL_TRANSACTION_FLAG_UPDATE,
view->external);
}
if (t->new_cache_file_seq != 0) {
ret = log_append_buffer(file, get_cache_reset_buf(t), NULL,
MAIL_TRANSACTION_CACHE_RESET,
view->external);
}
if (t->cache_updates != NULL && ret == 0) {
ret = log_append_buffer(file, t->cache_updates, NULL,
MAIL_TRANSACTION_CACHE_UPDATE,
view->external);
}
if (t->extra_rec_updates == NULL) {
updates = NULL;
size = 0;
} else {
updates = buffer_get_modifyable_data(t->extra_rec_updates,
&size);
size /= sizeof(*updates);
}
hdr_buf = buffer_create_data(pool_datastack_create(),
&extra_rec_hdr, sizeof(extra_rec_hdr));
buffer_set_used_size(hdr_buf, sizeof(extra_rec_hdr));
for (i = 0; i < size && ret == 0; i++) {
if (updates[i] == NULL)
continue;
if (!mail_index_map_get_extra_info_idx(index->map, i, &idx)) {
/* new one */
ret = mail_transaction_log_register_extra(file, t, i,
&idx);
if (ret < 0)
break;
}
extra_rec_hdr.data_id = idx;
ret = log_append_buffer(file, updates[i], hdr_buf,
MAIL_TRANSACTION_EXTRA_REC_UPDATE,
view->external);
}
if (t->expunges != NULL && ret == 0) {
ret = log_append_buffer(file, t->expunges, NULL,
MAIL_TRANSACTION_EXPUNGE,
view->external);
}
if (t->hdr_changed && ret == 0) {
ret = log_append_buffer(file, log_get_hdr_update_buffer(t),
NULL, MAIL_TRANSACTION_HEADER_UPDATE,
view->external);
}
if (ret < 0) {
mail_index_file_set_syscall_error(log->index, file->filepath,
"pwrite()");
}
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 && 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;
}
if (ret == 0 && file->first_append_size != 0) {
/* synced - rewrite first record's header */
ret = pwrite_full(file->fd, &file->first_append_size,
sizeof(uint32_t), append_offset);
if (ret < 0) {
mail_index_file_set_syscall_error(log->index,
file->filepath,
"pwrite()");
}
}
if (ret < 0)
file->sync_offset = append_offset;
*log_file_seq_r = file->hdr.file_seq;
*log_file_offset_r = file->sync_offset;
if (!log->index->log_locked)
mail_transaction_log_file_unlock(file);
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;
}