mail-transaction-log.c revision d9fdacd5fb3e07997e5c389739d2054f0c8441d8
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen/* this lock should never exist for a long time.. */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenmail_transaction_log_file_open_or_create(struct mail_transaction_log *log,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen const char *path);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenmail_transaction_log_file_read(struct mail_transaction_log_file *file,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenmail_transaction_log_file_set_corrupted(struct mail_transaction_log_file *file,
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen const char *fmt, ...)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (!MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen "Corrupted transaction log file %s: %s",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* this may have happened because of broken index.
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen make sure it's ok. */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenmail_transaction_log_file_dotlock(struct mail_transaction_log_file *file)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen ret = file_dotlock_create(&file->log->dotlock_settings,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen "file_dotlock_create()");
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen "Timeout while waiting for release of "
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen "dotlock for transaction log file %s",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenmail_transaction_log_file_undotlock(struct mail_transaction_log_file *file)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen ret = file_dotlock_delete(&file->log->dotlock);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen "Dotlock was lost for transaction log file %s",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenmail_transaction_log_file_lock(struct mail_transaction_log_file *file)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (file->log->index->lock_method == FILE_LOCK_METHOD_DOTLOCK)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen return mail_transaction_log_file_dotlock(file);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen ret = mail_index_lock_fd(file->log->index, file->filepath, file->fd,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen "mail_index_wait_lock_fd()");
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen "Timeout while waiting for lock for transaction log file %s",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenvoid mail_transaction_log_file_unlock(struct mail_transaction_log_file *file)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file))
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (file->log->index->lock_method == FILE_LOCK_METHOD_DOTLOCK) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen !(((file)->hdr.file_seq == (index)->hdr->log_file_seq && \
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen (index)->hdr->log_file_int_offset >= (file)->hdr.hdr_size) || \
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen ((file)->hdr.prev_file_seq == (index)->hdr->log_file_seq && \
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen (file)->hdr.prev_file_offset == (index)->hdr->log_file_int_offset))
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenstatic int mail_transaction_log_check_file_seq(struct mail_transaction_log *log)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen unsigned int lock_id;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen ret = mail_index_lock_shared(index, TRUE, &lock_id);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen else if (INDEX_HAS_MISSING_LOGS(index, file)) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* broken - fix it by creating a new log file */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenmail_transaction_log_open_or_create(struct mail_index *index)
2282944443f78bd186809df235da1b7e801f0430Phil Carmody log->dotlock_settings.timeout = LOG_DOTLOCK_TIMEOUT;
2282944443f78bd186809df235da1b7e801f0430Phil Carmody log->dotlock_settings.stale_timeout = LOG_DOTLOCK_STALE_TIMEOUT;
2282944443f78bd186809df235da1b7e801f0430Phil Carmody log->new_dotlock_settings = log->dotlock_settings;
2282944443f78bd186809df235da1b7e801f0430Phil Carmody log->new_dotlock_settings.lock_suffix = LOG_NEW_DOTLOCK_SUFFIX;
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody log->head = mail_transaction_log_file_open_or_create(log, path);
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody /* fallback to in-memory indexes */
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody log->head = mail_transaction_log_file_open_or_create(log, path);
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody /* head log file isn't same as head index file -
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody shouldn't happen except in race conditions.
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody lock them and check again */
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody if (mail_transaction_log_check_file_seq(log) < 0) {
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmodyvoid mail_transaction_log_close(struct mail_transaction_log **_log)
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainenmail_transaction_log_file_free(struct mail_transaction_log_file *file)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen for (p = &file->log->files; *p != NULL; p = &(*p)->next) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (munmap(file->mmap_base, file->mmap_size) < 0) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenint mail_transaction_log_move_to_memory(struct mail_transaction_log *log)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen struct mail_transaction_log_file *file = log->head;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (file == NULL || MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file))
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* read the whole file to memory. we might currently be appending
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen data into it, so we want to read it up to end of file */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (munmap(file->mmap_base, file->mmap_size) < 0) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (mail_transaction_log_file_read(file, 0) <= 0)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* after we've read the file into memory, make it into in-memory
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmodymail_transaction_log_file_read_hdr(struct mail_transaction_log_file *file,
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody i_assert(!MAIL_INDEX_IS_IN_MEMORY(file->log->index));
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen ret = pread_full(file->fd, &file->hdr, sizeof(file->hdr), 0);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail_index_file_set_syscall_error(file->log->index,
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen "pread_full()");
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen "unexpected end of file while reading header");
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen if (file->hdr.major_version != MAIL_TRANSACTION_LOG_MAJOR_VERSION) {
1d3ee23f5f573d5e5ab79422c0a23dab85f3d034Timo Sirainen /* incompatible version - fix silently */
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody if (file->hdr.hdr_size < MAIL_TRANSACTION_LOG_HEADER_MIN_SIZE) {
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody "Header size too small");
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody /* @UNSAFE: smaller than we expected - zero out the fields we
2442c230b6c95b20d26ead752f9de1cb40544a53Phil Carmody shouldn't have filled */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen memset(PTR_OFFSET(&file->hdr, file->hdr.hdr_size), 0,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* corrupted */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen "Transaction log file %s: marked corrupted",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (file->hdr.indexid != file->log->index->indexid) {
87b5c1fc03945708726c175d540fd307f7f78480Phil Carmody /* index file was probably just rebuilt and we don't
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen know about it yet */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen "invalid indexid (%u != %u)",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen file->hdr.indexid, file->log->index->indexid);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* creating index file. since transaction log is created
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen first, use the indexid in it to create the main index
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen to avoid races. */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen file->log->index->indexid = file->hdr.indexid;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* make sure we already don't have a file with the same sequence
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen opened. it shouldn't happen unless the old log file was
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen If we're opening head log file, make sure the sequence is larger
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen than any existing one. */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen for (f = file->log->files; f != NULL; f = f->next) {
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen "invalid new transaction log sequence "
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen "(%u >= %u)",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen for (f = file->log->files; f != NULL; f = f->next) {
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen "old transaction log already opened "
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen "(%u == %u)",
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenmail_transaction_log_init_hdr(struct mail_transaction_log *log,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen unsigned int lock_id;
d868a04630bd7bfe9c1543a7c3f68703b3e276e4Timo Sirainen hdr->major_version = MAIL_TRANSACTION_LOG_MAJOR_VERSION;
d868a04630bd7bfe9c1543a7c3f68703b3e276e4Timo Sirainen hdr->minor_version = MAIL_TRANSACTION_LOG_MINOR_VERSION;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen hdr->hdr_size = sizeof(struct mail_transaction_log_header);
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* not creating index - make sure we have latest header */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (mail_index_lock_shared(index, TRUE, &lock_id) < 0)
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen hdr->prev_file_seq = index->hdr->log_file_seq;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen hdr->prev_file_offset = index->hdr->log_file_int_offset;
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen if (log->head != NULL && hdr->file_seq <= log->head->hdr.file_seq) {
573424407a2d3c1453638a643583a7cf10c129e1Phil Carmody /* make sure the sequence grows */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainenmail_transaction_log_file_create2(struct mail_transaction_log *log,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* log creation is locked now - see if someone already created it */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail_index_file_set_syscall_error(index, path,
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen } else if (st.st_ino == ino && CMP_DEV_T(st.st_dev, dev) &&
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* same file, still broken */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen /* file changed, use the new file */
46b823ac3bce2c0f9f0fc73911e48d3a77b04fbeTimo Sirainen /* fstat() failure, return after closing fd.. */
cc2954ad6d8ba0509b870d773ba4b6b16353763cTimo Sirainen mail_index_file_set_syscall_error(index, path, "open()");
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody if (mail_transaction_log_init_hdr(log, &hdr) < 0)
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody if (write_full(new_fd, &hdr, sizeof(hdr)) < 0) {
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody "write_full()");
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody /* keep two log files */
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody /* ignore the error. we don't care that much about the
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody second log file and we're going to overwrite this
8d4428cee277a9a84450a8c6771f7a4dc68638cfPhil Carmody first one. */
5433d56d338db3948bf7bc22ef1daaba147dbb5dPhil Carmody /* success */
5433d56d338db3948bf7bc22ef1daaba147dbb5dPhil Carmodymail_transaction_log_file_create(struct mail_transaction_log *log,
5433d56d338db3948bf7bc22ef1daaba147dbb5dPhil Carmody const char *path,
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody i_assert(!MAIL_INDEX_IS_IN_MEMORY(log->index));
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody /* the whole index directory was deleted, which means
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody the mailbox was deleted by another process.
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody fail silently. */
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody mail_index_file_set_syscall_error(log->index, log->index->dir,
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody /* With dotlocking we might already have path.lock created, so this
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody filename has to be different. */
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody fd = file_dotlock_open(&log->new_dotlock_settings, path, 0, &dotlock);
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody mail_index_file_set_syscall_error(log->index, path,
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody "file_dotlock_open()");
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody mail_index_file_set_syscall_error(log->index, path, "fchown()");
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody /* either fd gets used or the dotlock gets deleted and returned fd
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody is for the existing file */
b22dcc3c0c84919fe500cf877d8dfd9bddbef76fPhil Carmody fd = mail_transaction_log_file_create2(log, path, fd, &dotlock,
return fd;
struct mail_transaction_log_file **p;
*p = file;
struct mail_transaction_log_file **p;
*p = file;
bool ignore_estale)
int ret;
if (ret < 0) {
return ret;
static struct mail_transaction_log_file *
int ret;
!try_retry);
if (ret < 0) {
return NULL;
if (ret == 0) {
!try_retry);
if (ret <= 0) {
return NULL;
return file;
static struct mail_transaction_log_file *
return NULL;
return file;
static struct mail_transaction_log_file *
const char *path)
bool retry;
int fd;
path,
return NULL;
return NULL;
return file;
static struct mail_transaction_log_file *
const char *path)
return NULL;
if (ret > 0)
if (ret == 0)
return NULL;
return file;
bool retry;
int fd;
if (lock) {
bool create_if_needed)
const char *path;
const char *path;
if (ret <= 0) {
if (ret == 0) {
if (stale) {
const void *data;
if (hdr_size == 0) {
void *data;
int ret;
if (ret == 0) {
if (ret < 0) {
if (ret > 0)
if (ret == 0) {
if (use_mmap) {
if (!use_mmap) {
if (ret <= 0) {
return ret;
MADV_SEQUENTIAL) < 0) {
int ret = 0;
if (ret < 0)
return ret;