Mercurial > dovecot > original-hg > dovecot-1.2
changeset 6944:66b3e894041b HEAD
Support mmap_disable=yes and some error handling improvements.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 07 Dec 2007 16:10:19 +0200 |
parents | 0a93c4e07776 |
children | 5915aea5f070 |
files | src/plugins/fts-squat/squat-trie-private.h src/plugins/fts-squat/squat-trie.c src/plugins/fts-squat/squat-uidlist.c |
diffstat | 3 files changed, 270 insertions(+), 90 deletions(-) [+] |
line wrap: on
line diff
--- a/src/plugins/fts-squat/squat-trie-private.h Fri Dec 07 00:46:31 2007 +0200 +++ b/src/plugins/fts-squat/squat-trie-private.h Fri Dec 07 16:10:19 2007 +0200 @@ -118,13 +118,18 @@ char *path; int fd; + struct file_cache *file_cache; + uoff_t locked_file_size; + const void *data; + size_t data_size; void *mmap_base; size_t mmap_size; unsigned char default_normalize_map[256]; + unsigned int mmap_disable:1; unsigned int corrupted:1; };
--- a/src/plugins/fts-squat/squat-trie.c Fri Dec 07 00:46:31 2007 +0200 +++ b/src/plugins/fts-squat/squat-trie.c Fri Dec 07 16:10:19 2007 +0200 @@ -3,9 +3,11 @@ #include "lib.h" #include "array.h" #include "str.h" +#include "read-full.h" #include "istream.h" #include "ostream.h" #include "unichar.h" +#include "file-cache.h" #include "seq-range-array.h" #include "squat-uidlist.h" #include "squat-trie-private.h" @@ -13,9 +15,6 @@ #include <stdio.h> #include <stdlib.h> #include <unistd.h> -#include <ctype.h> -#include <fcntl.h> -#include <time.h> #include <sys/mman.h> #define DEFAULT_NORMALIZE_MAP_CHARS \ @@ -26,6 +25,11 @@ #define MAX_FAST_LEVEL 3 #define SEQUENTIAL_COUNT 46 +#define TRIE_BYTES_LEFT(n) \ + ((n) * SQUAT_PACK_MAX_SIZE) +#define TRIE_READAHEAD_SIZE \ + I_MAX(4096, 1 + 256 + TRIE_BYTES_LEFT(256)) + struct squat_trie_build_context { struct squat_trie *trie; struct ostream *output; @@ -130,6 +134,7 @@ trie->fd = -1; trie->lock_method = lock_method; trie->uidvalidity = uidvalidity; + trie->mmap_disable = mmap_disable; squat_trie_normalize_map_build(trie); return trie; } @@ -141,6 +146,11 @@ memset(&trie->root, 0, sizeof(trie->root)); memset(&trie->hdr, 0, sizeof(trie->hdr)); + trie->data = NULL; + trie->data_size = 0; + + if (trie->file_cache != NULL) + file_cache_free(&trie->file_cache); if (trie->mmap_size != 0) { if (munmap(trie->mmap_base, trie->mmap_size) < 0) i_error("munmap(%s) failed: %m", trie->path); @@ -342,6 +352,20 @@ } static int +trie_file_cache_read(struct squat_trie *trie, size_t offset, size_t size) +{ + if (trie->file_cache == NULL) + return 0; + + if (file_cache_read(trie->file_cache, offset, size) < 0) { + i_error("read(%s) failed: %m", trie->path); + return -1; + } + trie->data = file_cache_get_map(trie->file_cache, &trie->data_size); + return 0; +} + +static int node_read_children(struct squat_trie *trie, struct squat_node *node, int level) { const uint8_t *data, *end; @@ -355,21 +379,24 @@ i_assert(node->children_not_mapped); i_assert(!node->have_sequential); i_assert(trie->unmapped_child_count > 0); + i_assert(trie->data_size <= trie->locked_file_size); trie->unmapped_child_count--; node_offset = node->children.offset; node->children_not_mapped = FALSE; node->children.data = NULL; - if (unlikely(node_offset >= trie->mmap_size)) { + if (trie_file_cache_read(trie, node_offset, TRIE_READAHEAD_SIZE) < 0) + return -1; + if (unlikely(node_offset >= trie->data_size)) { squat_trie_set_corrupted(trie); return -1; } - data = CONST_PTR_OFFSET(trie->mmap_base, node_offset); - end = CONST_PTR_OFFSET(trie->mmap_base, trie->mmap_size); + data = CONST_PTR_OFFSET(trie->data, node_offset); + end = CONST_PTR_OFFSET(trie->data, trie->data_size); child_count = *data++; - if (unlikely(node_offset + child_count >= trie->mmap_size)) { + if (unlikely(node_offset + child_count >= trie->data_size)) { squat_trie_set_corrupted(trie); return -1; } @@ -404,7 +431,7 @@ } else { base_offset -= num >> 1; } - if (base_offset >= trie->mmap_size) { + if (base_offset >= trie->locked_file_size) { squat_trie_set_corrupted(trie); return -1; } @@ -447,6 +474,24 @@ dest = child->children.leaf_string = i_malloc(len); } + + if (trie->file_cache != NULL) { + /* the string may be long - + recalculate the end pos */ + size_t offset, size; + + offset = (const char *)data - + (const char *)trie->data; + size = len + TRIE_BYTES_LEFT(child_count - i); + + if (trie_file_cache_read(trie, offset, + size) < 0) + return -1; + data = CONST_PTR_OFFSET(trie->data, offset); + end = CONST_PTR_OFFSET(trie->data, + trie->data_size); + } + if (end - data < len) { squat_trie_set_corrupted(trie); return -1; @@ -455,6 +500,11 @@ data += len; } } + if (unlikely(data == end)) { + /* we should never get this far */ + squat_trie_set_corrupted(trie); + return -1; + } return 0; } @@ -1092,38 +1142,52 @@ static int squat_trie_map_header(struct squat_trie *trie) { + int ret; + if (trie->locked_file_size == 0) { /* newly created file */ - squat_trie_header_init(trie); - return 0; + return 1; } i_assert(trie->fd != -1); - if (trie->mmap_size != 0) { - if (munmap(trie->mmap_base, trie->mmap_size) < 0) - i_error("munmap(%s) failed: %m", trie->path); + if (trie->mmap_disable) { + ret = pread_full(trie->fd, &trie->hdr, sizeof(trie->hdr), 0); + if (ret <= 0) { + if (ret < 0) { + i_error("pread(%s) failed: %m", trie->path); + return -1; + } + i_error("Corrupted %s: File too small", trie->path); + return 0; + } + trie->data = NULL; + trie->data_size = 0; + } else { + if (trie->locked_file_size < sizeof(trie->hdr)) { + i_error("Corrupted %s: File too small", trie->path); + return 0; + } + if (trie->mmap_size != 0) { + if (munmap(trie->mmap_base, trie->mmap_size) < 0) + i_error("munmap(%s) failed: %m", trie->path); + } + + trie->mmap_size = trie->locked_file_size; + trie->mmap_base = mmap(NULL, trie->mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, trie->fd, 0); + if (trie->mmap_base == MAP_FAILED) { + trie->data = trie->mmap_base = NULL; + trie->data_size = trie->mmap_size = 0; + i_error("mmap(%s) failed: %m", trie->path); + return -1; + } + memcpy(&trie->hdr, trie->mmap_base, sizeof(trie->hdr)); + trie->data = trie->mmap_base; + trie->data_size = trie->mmap_size; } - trie->mmap_size = trie->locked_file_size; - trie->mmap_base = mmap(NULL, trie->mmap_size, PROT_READ | PROT_WRITE, - MAP_SHARED, trie->fd, 0); - if (trie->mmap_base == MAP_FAILED) { - trie->mmap_base = NULL; - trie->mmap_size = 0; - i_error("mmap(%s) failed: %m", trie->path); - return -1; - } - memcpy(&trie->hdr, trie->mmap_base, sizeof(trie->hdr)); - - if (trie->hdr.root_offset == 0) - return 0; - if (!squat_trie_check_header(trie)) { - squat_trie_delete(trie); - squat_trie_close(trie); - squat_trie_header_init(trie); - return 0; - } - return 0; + return squat_trie_check_header(trie) ? 1 : 0; } static int squat_trie_map(struct squat_trie *trie, bool building) @@ -1135,9 +1199,17 @@ if (trie->fd != -1) { if (squat_trie_lock(trie, F_RDLCK, &file_lock) <= 0) return -1; + if (trie->mmap_disable && trie->file_cache == NULL) + trie->file_cache = file_cache_new(trie->fd); } ret = squat_trie_map_header(trie); + if (ret == 0) { + file_lock_free(&file_lock); + squat_trie_delete(trie); + squat_trie_close(trie); + squat_trie_header_init(trie); + } changed = trie->root.children.offset != trie->hdr.root_offset; if (changed || trie->hdr.root_offset == 0) { @@ -1157,7 +1229,7 @@ } } - if (ret == 0 && !building) { + if (ret >= 0 && !building) { /* do this while we're still locked */ ret = squat_uidlist_refresh(trie->uidlist); } @@ -1267,6 +1339,10 @@ ret = squat_write_nodes(ctx); ctx->output = NULL; + /* write 1 byte guard at the end of file, so that we can verify broken + squat_unpack_num() input by checking if data==end */ + o_stream_send(output, "", 1); + if (trie->corrupted) ret = -1; if (ret == 0)
--- a/src/plugins/fts-squat/squat-uidlist.c Fri Dec 07 00:46:31 2007 +0200 +++ b/src/plugins/fts-squat/squat-uidlist.c Fri Dec 07 16:10:19 2007 +0200 @@ -3,9 +3,11 @@ #include "lib.h" #include "array.h" #include "bsearch-insert-pos.h" +#include "file-cache.h" #include "file-lock.h" +#include "read-full.h" +#include "write-full.h" #include "ostream.h" -#include "write-full.h" #include "squat-trie-private.h" #include "squat-uidlist.h" @@ -34,6 +36,7 @@ char *path; int fd; + struct file_cache *file_cache; struct file_lock *file_lock; uoff_t locked_file_size; @@ -42,6 +45,9 @@ size_t mmap_size; struct squat_uidlist_file_header hdr; + const void *data; + size_t data_size; + unsigned int cur_block_count; const uint32_t *cur_block_offsets; const uint32_t *cur_block_end_indexes; @@ -263,32 +269,72 @@ return ret; } -static int node_uidlist_map_blocks(struct squat_uidlist *uidlist) +static void squat_uidlist_map_blocks_set_pointers(struct squat_uidlist *uidlist) +{ + const void *base; + size_t end_index_size, end_size; + + base = CONST_PTR_OFFSET(uidlist->data, uidlist->hdr.block_list_offset + + sizeof(uint32_t)); + + end_index_size = uidlist->cur_block_count * sizeof(uint32_t); + end_size = end_index_size + uidlist->cur_block_count * sizeof(uint32_t); + if (end_size <= uidlist->data_size) { + uidlist->cur_block_end_indexes = base; + uidlist->cur_block_offsets = + CONST_PTR_OFFSET(base, end_index_size); + } else { + uidlist->cur_block_end_indexes = NULL; + uidlist->cur_block_offsets = NULL; + } +} + +static int uidlist_file_cache_read(struct squat_uidlist *uidlist, + size_t offset, size_t size) +{ + if (uidlist->file_cache == NULL) + return 0; + + if (file_cache_read(uidlist->file_cache, offset, size) < 0) { + i_error("read(%s) failed: %m", uidlist->path); + return -1; + } + uidlist->data = file_cache_get_map(uidlist->file_cache, + &uidlist->data_size); + squat_uidlist_map_blocks_set_pointers(uidlist); + return 0; +} + +static int squat_uidlist_map_blocks(struct squat_uidlist *uidlist) { const struct squat_uidlist_file_header *hdr = &uidlist->hdr; const void *base; - uint32_t block_count, block_end_offset, i, verify_count; + uint32_t block_count, blocks_offset, blocks_size, i, verify_count; - block_end_offset = hdr->block_list_offset + sizeof(block_count); - if (block_end_offset > uidlist->mmap_size) { + /* get number of blocks */ + if (uidlist_file_cache_read(uidlist, hdr->block_list_offset, + sizeof(block_count)) < 0) + return -1; + blocks_offset = hdr->block_list_offset + sizeof(block_count); + if (blocks_offset > uidlist->data_size) { squat_uidlist_set_corrupted(uidlist, "block list outside file"); return -1; } - base = CONST_PTR_OFFSET(uidlist->mmap_base, hdr->block_list_offset); + base = CONST_PTR_OFFSET(uidlist->data, hdr->block_list_offset); memcpy(&block_count, base, sizeof(block_count)); - base = CONST_PTR_OFFSET(base, sizeof(block_count)); - block_end_offset += block_count * sizeof(uint32_t)*2; - if (block_end_offset > uidlist->mmap_size) { + /* map the blocks */ + blocks_size = block_count * sizeof(uint32_t)*2; + if (uidlist_file_cache_read(uidlist, blocks_offset, blocks_size) < 0) + return -1; + if (blocks_offset + blocks_size > uidlist->data_size) { squat_uidlist_set_corrupted(uidlist, "block list outside file"); return -1; } uidlist->cur_block_count = block_count; - uidlist->cur_block_end_indexes = base; - uidlist->cur_block_offsets = - CONST_PTR_OFFSET(base, block_count * sizeof(uint32_t)); + squat_uidlist_map_blocks_set_pointers(uidlist); /* verify just a couple of the end indexes to make sure they look correct */ @@ -319,13 +365,25 @@ return -1; } if (uidlist->hdr.used_file_size < sizeof(uidlist->hdr) || - uidlist->hdr.used_file_size > uidlist->mmap_size) { + (uidlist->hdr.used_file_size > uidlist->mmap_size && + uidlist->mmap_base != NULL)) { squat_uidlist_set_corrupted(uidlist, "broken used_file_size"); return -1; } - if (node_uidlist_map_blocks(uidlist) < 0) - return -1; - return 0; + return squat_uidlist_map_blocks(uidlist); +} + +static void squat_uidlist_unmap(struct squat_uidlist *uidlist) +{ + if (uidlist->mmap_size != 0) { + if (munmap(uidlist->mmap_base, uidlist->mmap_size) < 0) + i_error("munmap(%s) failed: %m", uidlist->path); + uidlist->mmap_base = NULL; + uidlist->mmap_size = 0; + } + uidlist->cur_block_count = 0; + uidlist->cur_block_end_indexes = NULL; + uidlist->cur_block_offsets = NULL; } static int squat_uidlist_mmap(struct squat_uidlist *uidlist) @@ -343,26 +401,26 @@ return -1; } - if (uidlist->mmap_size != 0) { - if (munmap(uidlist->mmap_base, uidlist->mmap_size) < 0) - i_error("munmap(%s) failed: %m", uidlist->path); - } + squat_uidlist_unmap(uidlist); uidlist->mmap_size = st.st_size; uidlist->mmap_base = mmap(NULL, uidlist->mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, uidlist->fd, 0); if (uidlist->mmap_base == MAP_FAILED) { - uidlist->mmap_base = NULL; - uidlist->mmap_size = 0; + uidlist->data = uidlist->mmap_base = NULL; + uidlist->data_size = uidlist->mmap_size = 0; i_error("mmap(%s) failed: %m", uidlist->path); return -1; } + uidlist->data = uidlist->mmap_base; + uidlist->data_size = uidlist->mmap_size; return 0; } static int squat_uidlist_map(struct squat_uidlist *uidlist) { const struct squat_uidlist_file_header *mmap_hdr = uidlist->mmap_base; + int ret; if (mmap_hdr != NULL && !uidlist->building && uidlist->hdr.block_list_offset == mmap_hdr->block_list_offset) { @@ -370,15 +428,36 @@ return 0; } - if (mmap_hdr == NULL || uidlist->building || - uidlist->mmap_size < mmap_hdr->used_file_size) { - if (squat_uidlist_mmap(uidlist) < 0) - return -1; + if (!uidlist->trie->mmap_disable) { + if (mmap_hdr == NULL || uidlist->building || + uidlist->mmap_size < mmap_hdr->used_file_size) { + if (squat_uidlist_mmap(uidlist) < 0) + return -1; + } + + if (!uidlist->building) { + memcpy(&uidlist->hdr, uidlist->mmap_base, + sizeof(uidlist->hdr)); + } + } else if (uidlist->building) { + /* we want to update blocks mapping, but using the header + in memory */ + } else { + ret = pread_full(uidlist->fd, &uidlist->hdr, + sizeof(uidlist->hdr), 0); + if (ret <= 0) { + if (ret < 0) { + i_error("pread(%s) failed: %m", uidlist->path); + return -1; + } + i_error("Corrupted %s: File too small", uidlist->path); + return 0; + } + uidlist->data = NULL; + uidlist->data_size = 0; } - - if (!uidlist->building) - memcpy(&uidlist->hdr, uidlist->mmap_base, sizeof(uidlist->hdr)); - + if (uidlist->file_cache == NULL && uidlist->trie->mmap_disable) + uidlist->file_cache = file_cache_new(uidlist->fd); return squat_uidlist_map_header(uidlist); } @@ -422,26 +501,23 @@ { i_assert(!uidlist->building); + squat_uidlist_unmap(uidlist); + if (uidlist->file_cache != NULL) + file_cache_free(&uidlist->file_cache); if (uidlist->file_lock != NULL) file_lock_free(&uidlist->file_lock); - if (uidlist->mmap_size != 0) { - if (munmap(uidlist->mmap_base, uidlist->mmap_size) < 0) - i_error("munmap(%s) failed: %m", uidlist->path); - uidlist->mmap_base = NULL; - uidlist->mmap_size = 0; - } if (uidlist->fd != -1) { if (close(uidlist->fd) < 0) i_error("close(%s) failed: %m", uidlist->path); uidlist->fd = -1; } - uidlist->cur_block_end_indexes = NULL; - uidlist->cur_block_offsets = NULL; uidlist->corrupted = FALSE; } int squat_uidlist_refresh(struct squat_uidlist *uidlist) { + /* we assume here that trie is locked, so that we don't need to worry + about it when reading the header */ if (uidlist->fd == -1 || uidlist->hdr.indexid != uidlist->trie->hdr.indexid) { if (squat_uidlist_open(uidlist) < 0) @@ -1080,26 +1156,38 @@ } static int -node_uidlist_get_at_offset(struct squat_uidlist *uidlist, uoff_t offset, - uint32_t num, ARRAY_TYPE(uint32_t) *uids) +squat_uidlist_get_at_offset(struct squat_uidlist *uidlist, uoff_t offset, + uint32_t num, ARRAY_TYPE(uint32_t) *uids) { const uint8_t *p, *end; uint32_t size, base_uid; + uoff_t uidlist_data_offset; unsigned int i, j, extra = 0; - p = CONST_PTR_OFFSET(uidlist->mmap_base, offset); - end = CONST_PTR_OFFSET(uidlist->mmap_base, uidlist->mmap_size); + if (num != 0) + uidlist_data_offset = offset; + else { + /* not given, read it */ + if (uidlist_file_cache_read(uidlist, offset, + SQUAT_PACK_MAX_SIZE) < 0) + return -1; - if (num == 0) { - /* not given, read it */ + p = CONST_PTR_OFFSET(uidlist->data, offset); + end = CONST_PTR_OFFSET(uidlist->data, uidlist->data_size); num = squat_unpack_num(&p, end); + uidlist_data_offset = p - (const uint8_t *)uidlist->data; } size = num >> 2; - if (p + size > end) { + + if (uidlist_file_cache_read(uidlist, uidlist_data_offset, size) < 0) + return -1; + if (uidlist_data_offset + size > uidlist->data_size) { squat_uidlist_set_corrupted(uidlist, "size points outside file"); return -1; } + + p = CONST_PTR_OFFSET(uidlist->data, uidlist_data_offset); end = p + size; if ((num & UIDLIST_PACKED_FLAG_BEGINS_WITH_POINTER) != 0) { @@ -1113,8 +1201,8 @@ return -1; } else { prev = offset - (prev >> 1); - if (node_uidlist_get_at_offset(uidlist, prev, - 0, uids) < 0) + if (squat_uidlist_get_at_offset(uidlist, prev, + 0, uids) < 0) return -1; } } @@ -1160,12 +1248,13 @@ } static int -node_uidlist_get_offset(struct squat_uidlist *uidlist, uint32_t uid_list_idx, - uint32_t *offset_r, uint32_t *num_r) +squat_uidlist_get_offset(struct squat_uidlist *uidlist, uint32_t uid_list_idx, + uint32_t *offset_r, uint32_t *num_r) { const uint8_t *p, *end; unsigned int idx; uint32_t num, skip_bytes, uidlists_offset; + size_t max_map_size; if (uidlist->fd == -1) { squat_uidlist_set_corrupted(uidlist, "no uidlists"); @@ -1186,14 +1275,19 @@ return -1; } + /* make sure everything is mapped */ + uid_list_idx -= idx == 0 ? 0 : uidlist->cur_block_end_indexes[idx-1]; + max_map_size = SQUAT_PACK_MAX_SIZE * (1+uid_list_idx); + if (uidlist_file_cache_read(uidlist, uidlist->cur_block_offsets[idx], + max_map_size) < 0) + return -1; + /* find the uidlist inside the block */ - p = CONST_PTR_OFFSET(uidlist->mmap_base, - uidlist->cur_block_offsets[idx]); - end = CONST_PTR_OFFSET(uidlist->mmap_base, uidlist->mmap_size); + p = CONST_PTR_OFFSET(uidlist->data, uidlist->cur_block_offsets[idx]); + end = CONST_PTR_OFFSET(uidlist->data, uidlist->data_size); uidlists_offset = uidlist->cur_block_offsets[idx] - squat_unpack_num(&p, end); - uid_list_idx -= idx == 0 ? 0 : uidlist->cur_block_end_indexes[idx-1]; for (skip_bytes = 0; uid_list_idx > 0; uid_list_idx--) { num = squat_unpack_num(&p, end); skip_bytes += num >> 2; @@ -1201,7 +1295,12 @@ *offset_r = uidlists_offset + skip_bytes; *num_r = squat_unpack_num(&p, end); - if (unlikely(*offset_r > uidlist->mmap_size)) { + if (unlikely(p == end)) { + squat_uidlist_set_corrupted(uidlist, "broken file"); + return -1; + } + if (unlikely(*offset_r > uidlist->mmap_size && + uidlist->mmap_base != NULL)) { squat_uidlist_set_corrupted(uidlist, "broken offset"); return -1; } @@ -1229,9 +1328,9 @@ } uid_list_idx = (uid_list_idx >> 1) - 0x100; - if (node_uidlist_get_offset(uidlist, uid_list_idx, &offset, &num) < 0) + if (squat_uidlist_get_offset(uidlist, uid_list_idx, &offset, &num) < 0) return -1; - return node_uidlist_get_at_offset(uidlist, offset, num, uids); + return squat_uidlist_get_at_offset(uidlist, offset, num, uids); } uint32_t squat_uidlist_singleton_last_uid(uint32_t uid_list_idx)