Mercurial > dovecot > original-hg > dovecot-1.2
changeset 5784:9493c7f1ebca HEAD
Updating index file by overwriting changed parts didn't work correctly.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 20 Jun 2007 01:34:18 +0300 |
parents | 92bc0a7580f6 |
children | bcf58c66a099 |
files | src/lib-index/mail-index-map.c src/lib-index/mail-index-private.h src/lib-index/mail-index-write.c |
diffstat | 3 files changed, 93 insertions(+), 27 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-index/mail-index-map.c Wed Jun 20 01:08:13 2007 +0300 +++ b/src/lib-index/mail-index-map.c Wed Jun 20 01:34:18 2007 +0300 @@ -697,8 +697,13 @@ return ret; } + index->last_read_log_file_seq = new_map->hdr.log_file_seq; + index->last_read_log_file_head_offset = + new_map->hdr.log_file_head_offset; index->last_read_log_file_tail_offset = new_map->hdr.log_file_tail_offset; + index->last_read_stat = st; + mail_index_unmap(index, map); *map = new_map; return 1;
--- a/src/lib-index/mail-index-private.h Wed Jun 20 01:08:13 2007 +0300 +++ b/src/lib-index/mail-index-private.h Wed Jun 20 01:34:18 2007 +0300 @@ -6,6 +6,8 @@ #include "mail-index-view-private.h" #include "mail-index-transaction-private.h" +#include <sys/stat.h> + struct mail_transaction_header; struct mail_transaction_log_view; struct mail_index_sync_map_ctx; @@ -158,9 +160,14 @@ struct mail_index_map *map; uint32_t indexid; - /* last known log_file_tail_offset in main index file. used for - optimizing main index updates. */ + /* last_read_log_file_* contains the seq/offsets we last read from + the main index file's headers. these are used ro figure out when + the main index file should be updated, and if we can update it + by writing on top of it or if we need to recreate it. */ + uint32_t last_read_log_file_seq; + uint32_t last_read_log_file_head_offset; uint32_t last_read_log_file_tail_offset; + struct stat last_read_stat; int lock_type, shared_lock_count, excl_lock_count; unsigned int lock_id;
--- a/src/lib-index/mail-index-write.c Wed Jun 20 01:08:13 2007 +0300 +++ b/src/lib-index/mail-index-write.c Wed Jun 20 01:34:18 2007 +0300 @@ -1,12 +1,17 @@ /* Copyright (C) 2003-2007 Timo Sirainen */ #include "lib.h" +#include "read-full.h" #include "write-full.h" #include "mail-index-private.h" #include "mail-transaction-log-private.h" #include <stdio.h> +#define MAIL_INDEX_MIN_UPDATE_SIZE 1024 +/* if we're updating >= count-n messages, recreate the index */ +#define MAIL_INDEX_MAX_OVERWRITE_NEG_SEQ_COUNT 10 + static int mail_index_recreate(struct mail_index *index) { struct mail_index_map *map = index->map; @@ -68,27 +73,6 @@ if (MAIL_INDEX_IS_IN_MEMORY(index)) return 0; - /* write records. */ - if (map->write_seq_first != 0) { - size_t rec_offset = - (map->write_seq_first-1) * map->hdr.record_size; - - if (pwrite_full(index->fd, - CONST_PTR_OFFSET(map->records, rec_offset), - (map->write_seq_last - - map->write_seq_first + 1) * - map->hdr.record_size, - map->hdr.header_size + rec_offset) < 0) - return -1; - } - - /* write base header. it has changed practically always, so - map->write_base_header might not be TRUE here in all situations. - It's used only to figure out if we want to write the map at all. */ - base_size = I_MIN(map->hdr.base_header_size, sizeof(map->hdr)); - if (pwrite_full(index->fd, &map->hdr, base_size, 0) < 0) - return -1; - /* write extended headers */ if (map->write_ext_header) { base_size = map->hdr.base_header_size; @@ -98,9 +82,51 @@ base_size) < 0) return -1; } + + /* write records. */ + if (map->write_seq_first != 0) { + size_t rec_offset = + (map->write_seq_first-1) * map->hdr.record_size; + size_t recs_size = map->hdr.record_size * + (map->write_seq_last - map->write_seq_first + 1); + + if (pwrite_full(index->fd, + CONST_PTR_OFFSET(map->records, rec_offset), + recs_size, + map->hdr.header_size + rec_offset) < 0) + return -1; + } + + /* Write base header last. If we happen to crash in above pwrites, it + doesn't matter because we haven't yet written log file offsets, so + all the changes will be re-applied and the header/data state will + stay valid. + + The base header changes practically always, so + map->write_base_header might not be TRUE here in all situations. + It's used only to figure out if we want to write the map at all. */ + base_size = I_MIN(map->hdr.base_header_size, sizeof(map->hdr)); + if (pwrite_full(index->fd, &map->hdr, base_size, 0) < 0) + return -1; return 0; } +static bool mail_index_has_last_changed(struct mail_index *index) +{ + struct mail_index_header hdr; + int ret; + + if ((ret = pread_full(index->fd, &hdr, sizeof(hdr), 0)) <= 0) { + if (ret < 0 && errno != ESTALE) + mail_index_set_syscall_error(index, "pread_full()"); + return TRUE; + } + + return hdr.log_file_head_offset != + index->last_read_log_file_head_offset || + hdr.log_file_seq != index->last_read_log_file_seq; +} + #define mail_index_map_has_changed(map) \ ((map)->write_base_header || (map)->write_ext_header || \ (map)->write_seq_first != 0) @@ -109,6 +135,7 @@ { struct mail_index_map *map = index->map; const struct mail_index_header *hdr = &map->hdr; + struct stat st; unsigned int lock_id; if (!mail_index_map_has_changed(map)) @@ -118,15 +145,41 @@ /* header size growed. we can't update this file anymore. */ map->write_atomic = TRUE; } - if (index->fd == -1) { + if (index->fd == -1 || index->last_read_log_file_seq == 0) { /* index file doesn't exist, it's corrupted or we haven't opened it for some reason */ map->write_atomic = TRUE; } + + if (index->last_read_stat.st_size < MAIL_INDEX_MIN_UPDATE_SIZE || + (map->write_seq_last - map->write_seq_first + 1) + + MAIL_INDEX_MAX_OVERWRITE_NEG_SEQ_COUNT >= map->records_count) { + /* the file is so small that we don't even bother trying to + update it / changes are so large we might as well recreate */ + map->write_atomic = TRUE; + } + + if (!map->write_atomic) { + /* we can't update the file unless it's the same as it was + when we last read it. this is the first quick check before + locking. */ + if (stat(index->filepath, &st) < 0) { + if (errno != ENOENT) + mail_index_set_syscall_error(index, "stat()"); + map->write_atomic = TRUE; + } else if (st.st_ino != index->last_read_stat.st_ino || + !CMP_ST_CTIME(&st, &index->last_read_stat)) + map->write_atomic = TRUE; + } + if (!map->write_atomic) { if (mail_index_try_lock_exclusive(index, &lock_id) <= 0) { - /* locking failed, rewrite */ + /* locking failed, recreate */ map->write_atomic = TRUE; + } else if (mail_index_has_last_changed(index)) { + /* changed, we can't trust updating it anymore */ + map->write_atomic = TRUE; + mail_index_unlock(index, lock_id); } } @@ -139,13 +192,14 @@ } } else { if (mail_index_write_map_over(index) < 0) { - mail_index_set_error(index, - "pwrite_full(%s) failed: %m", index->filepath); + mail_index_set_syscall_error(index, "pwrite_full()"); mail_index_set_inconsistent(index); } mail_index_unlock(index, lock_id); } + index->last_read_log_file_seq = hdr->log_file_seq; + index->last_read_log_file_head_offset = hdr->log_file_head_offset; index->last_read_log_file_tail_offset = hdr->log_file_tail_offset; map->write_atomic = FALSE;