Mercurial > dovecot > core-2.2
diff src/lib-index/mail-index.c @ 2563:5752e5ac88f0 HEAD
Save extra record/header infos into index file permanently.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 05 Sep 2004 20:53:45 +0300 |
parents | 1fed92b6cc01 |
children | a9599d2f1af6 |
line wrap: on
line diff
--- a/src/lib-index/mail-index.c Sun Sep 05 20:33:19 2004 +0300 +++ b/src/lib-index/mail-index.c Sun Sep 05 20:53:45 2004 +0300 @@ -26,12 +26,11 @@ index->prefix = i_strdup(prefix); index->fd = -1; - index->extra_records_pool = - pool_alloconly_create("extra_record_pool", 256); - index->extra_records_buf = - buffer_create_dynamic(index->extra_records_pool, + index->extra_infos_pool = + pool_alloconly_create("extra_infos_pool", 256); + index->extra_infos = + buffer_create_dynamic(index->extra_infos_pool, 64, (size_t)-1); - index->max_record_size = sizeof(struct mail_index_record); index->mode = 0600; index->gid = (gid_t)-1; @@ -41,7 +40,7 @@ void mail_index_free(struct mail_index *index) { mail_index_close(index); - pool_unref(index->extra_records_pool); + pool_unref(index->extra_infos_pool); i_free(index->error); i_free(index->dir); @@ -50,42 +49,171 @@ } uint32_t mail_index_register_record_extra(struct mail_index *index, - const char *name, uint16_t size) + const char *name, uint32_t hdr_size, + uint16_t record_size) { + const struct mail_index_extra_record_info *einfos; struct mail_index_extra_record_info info; - size_t buf_size; + size_t extra_count; unsigned int i; + einfos = buffer_get_data(index->extra_infos, &extra_count); + extra_count /= sizeof(*einfos); + /* see if it's there already */ - for (i = 0; i < index->extra_records_count; i++) { - if (strcmp(index->extra_records[i].name, name) == 0) { - i_assert(index->extra_records[i].size == size); + for (i = 0; i < extra_count; i++) { + if (strcmp(einfos[i].name, name) == 0) { + i_assert(einfos[i].hdr_size == hdr_size); + i_assert(einfos[i].record_size == record_size); return i; } } - i_assert(size % 4 == 0); - i_assert(index->max_record_size + size <= 65535); - - if (index->extra_records_count >= MAIL_INDEX_MAX_EXTRA_RECORDS) { - i_panic("Maximum extra record count reached, " - "you'll need to recompile with larger limit. " - "MAIL_INDEX_MAX_EXTRA_RECORDS = %d", - MAIL_INDEX_MAX_EXTRA_RECORDS); - } + i_assert(hdr_size % 4 == 0); + i_assert(record_size % 4 == 0); memset(&info, 0, sizeof(info)); - info.name = p_strdup(index->extra_records_pool, name); - info.size = size; - info.offset = index->max_record_size; + info.name = p_strdup(index->extra_infos_pool, name); + info.hdr_size = hdr_size; + info.record_size = record_size; + + buffer_append(index->extra_infos, &info, sizeof(info)); + return extra_count; +} + +static void mail_index_map_create_extra_infos(struct mail_index_map *map, + unsigned int initial_count) +{ + size_t extra_infos_size, extra_infos_id_map_size, size; + + extra_infos_size = initial_count * + sizeof(struct mail_index_extra_record_info); + extra_infos_id_map_size = initial_count * sizeof(uint32_t); + if (map->extra_records_pool == NULL) { + size = extra_infos_size + extra_infos_id_map_size + + initial_count * 20; + map->extra_records_pool = + pool_alloconly_create("extra_infos", + nearest_power(size)); + } + + map->extra_infos = buffer_create_dynamic(map->extra_records_pool, + extra_infos_size, (size_t)-1); + map->extra_infos_id_map = buffer_create_dynamic(map->extra_records_pool, + extra_infos_id_map_size, + (size_t)-1); +} + +uint32_t mail_index_map_register_extra_info(struct mail_index *index, + struct mail_index_map *map, + const char *name, + uint32_t hdr_offset, + uint32_t hdr_size, + uint32_t record_size) +{ + const struct mail_index_extra_record_info *last_einfo; + struct mail_index_extra_record_info *einfo; + size_t size; + uint32_t idx, data_id; + + if (map->extra_infos == NULL) { + mail_index_map_create_extra_infos(map, 5); + last_einfo = NULL; + idx = 0; + } else { + last_einfo = buffer_get_data(map->extra_infos, &size); + idx = size / sizeof(*last_einfo); + if (idx == 0) + last_einfo = NULL; + else + last_einfo += idx - 1; + } + + einfo = buffer_append_space_unsafe(map->extra_infos, sizeof(*einfo)); + memset(einfo, 0, sizeof(*einfo)); + + einfo->name = p_strdup(map->extra_records_pool, name); + einfo->hdr_offset = hdr_offset; + einfo->hdr_size = hdr_size; + einfo->record_size = record_size; + + if (last_einfo != NULL) { + einfo->record_offset = last_einfo->record_offset + + last_einfo->record_size; + } else { + einfo->record_offset = sizeof(struct mail_index_record); + } - buffer_append(index->extra_records_buf, &info, sizeof(info)); - index->extra_records = - buffer_get_data(index->extra_records_buf, &buf_size); - index->extra_records_count = buf_size / sizeof(info); + data_id = mail_index_register_record_extra(index, name, + hdr_size, record_size); + buffer_write(map->extra_infos_id_map, data_id * sizeof(uint32_t), + &idx, sizeof(idx)); + return idx; +} + +static int mail_index_read_extra_infos(struct mail_index *index, + struct mail_index_map *map) +{ + const struct mail_index_extra_record_info_header *einfo_hdr; + unsigned int i, old_count; + const char *name; + uint32_t data_id, offset, name_offset; + + offset = map->hdr->base_header_size; + if (offset == map->hdr->header_size && + map->extra_records_pool == NULL) { + /* nothing to do, skip allocatations and all */ + return 1; + } + + old_count = index->extra_infos->used / + sizeof(struct mail_index_extra_record_info); + + if (map->extra_records_pool != NULL) + p_clear(map->extra_records_pool); + mail_index_map_create_extra_infos(map, old_count + 5); + data_id = (uint32_t)-1; + for (i = 0; i < old_count; i++) { + buffer_append(map->extra_infos_id_map, + &data_id, sizeof(data_id)); + } - index->max_record_size += size; - return index->extra_records_count-1; + name = map->hdr_base; + while (offset < map->hdr->header_size) { + name_offset = offset; + + while (offset < map->hdr->header_size && name[offset] != '\0') + offset++; + if (offset == map->hdr->header_size) { + mail_index_set_error(index, "Corrupted index file %s: " + "Header extension name doesn't end with NUL", + index->filepath); + return -1; + } + offset++; + while (offset < map->hdr->header_size && (offset % 4) != 0) + offset++; + + einfo_hdr = CONST_PTR_OFFSET(map->hdr_base, offset); + + if (offset + sizeof(*einfo_hdr) > map->hdr->header_size || + offset + sizeof(*einfo_hdr) + einfo_hdr->hdr_size > + map->hdr->header_size) { + mail_index_set_error(index, "Corrupted index file %s: " + "Header extension goes outside header", + index->filepath); + return -1; + } + + mail_index_map_register_extra_info(index, map, + name + name_offset, + offset + sizeof(*einfo_hdr), + einfo_hdr->hdr_size, + einfo_hdr->record_size); + + offset += sizeof(*einfo_hdr) + einfo_hdr->hdr_size; + } + return 1; } static int mail_index_check_header(struct mail_index *index, @@ -151,7 +279,7 @@ hdr->first_deleted_uid_lowwater > hdr->next_uid) return 0; - return 1; + return mail_index_read_extra_infos(index, map); } static void mail_index_map_clear(struct mail_index *index, @@ -184,6 +312,9 @@ i_assert(map->refcount == 0); mail_index_map_clear(index, map); + if (map->extra_records_pool != NULL) + pool_unref(map->extra_records_pool); + buffer_free(map->hdr_copy_buf); i_free(map); } @@ -197,6 +328,7 @@ static int mail_index_mmap(struct mail_index *index, struct mail_index_map *map) { const struct mail_index_header *hdr; + struct mail_index_header *mhdr; unsigned int records_count; i_assert(!map->write_to_disk); @@ -241,25 +373,35 @@ if (map->hdr->base_header_size < sizeof(*map->hdr)) { /* header smaller than ours, make a copy so our newer headers won't have garbage in them */ - memcpy(&map->hdr_copy, map->hdr, map->hdr->base_header_size); - map->hdr = &map->hdr_copy; - } + buffer_reset(map->hdr_copy_buf); + buffer_append(map->hdr_copy_buf, + map->hdr, map->hdr->base_header_size); + buffer_set_used_size(map->hdr_copy_buf, sizeof(*map->hdr)); + mhdr = buffer_get_modifyable_data(map->hdr_copy_buf, NULL); + mhdr->base_header_size = sizeof(*map->hdr); + mhdr->header_size = map->hdr_copy_buf->used; + + map->hdr = mhdr; + } + + map->hdr_base = map->mmap_base; map->records = PTR_OFFSET(map->mmap_base, map->hdr->header_size); map->records_count = map->hdr->messages_count; return 1; } static int mail_index_read_map(struct mail_index *index, - struct mail_index_map *map) + struct mail_index_map *map, int *retry_r) { - struct mail_index_header hdr; + struct mail_index_header hdr, *hdrp; void *data = NULL; ssize_t ret; size_t pos, records_size; i_assert(map->mmap_base == NULL); + *retry_r = FALSE; memset(&hdr, 0, sizeof(hdr)); ret = 1; @@ -269,7 +411,50 @@ if (ret > 0) pos += ret; } - if (ret >= 0 && pos >= MAIL_INDEX_HEADER_MIN_SIZE) { + + if (ret >= 0 && pos >= MAIL_INDEX_HEADER_MIN_SIZE && + (ret > 0 || pos >= hdr.base_header_size)) { + if (hdr.base_header_size < MAIL_INDEX_HEADER_MIN_SIZE || + hdr.header_size < hdr.base_header_size) { + mail_index_set_error(index, "Corrupted index file %s: " + "Corrupted header sizes (base %u, full %u)", + index->filepath, hdr.base_header_size, + hdr.header_size); + return 0; + } + + buffer_reset(map->hdr_copy_buf); + if (hdr.base_header_size < sizeof(hdr)) { + buffer_append(map->hdr_copy_buf, &hdr, + hdr.base_header_size); + buffer_set_used_size(map->hdr_copy_buf, sizeof(hdr) + + hdr.header_size - + hdr.base_header_size); + + /* @UNSAFE */ + ret = pread_full(index->fd, + PTR_OFFSET(map->hdr_copy_buf->data, + sizeof(hdr)), + hdr.header_size - hdr.base_header_size, + hdr.base_header_size); + + hdrp = buffer_get_modifyable_data(map->hdr_copy_buf, + NULL); + hdrp->base_header_size = sizeof(hdr); + hdrp->header_size = map->hdr_copy_buf->used; + } else { + buffer_append(map->hdr_copy_buf, &hdr, pos); + buffer_set_used_size(map->hdr_copy_buf, + hdr.header_size); + /* @UNSAFE */ + ret = pread_full(index->fd, + PTR_OFFSET(map->hdr_copy_buf->data, + pos), + hdr.header_size - pos, pos); + } + } + + if (ret > 0) { records_size = hdr.messages_count * hdr.record_size; if (map->buffer == NULL) { @@ -287,33 +472,36 @@ } if (ret < 0) { - if (errno == ESTALE) + if (errno == ESTALE) { + *retry_r = TRUE; return 0; + } mail_index_set_syscall_error(index, "pread_full()"); return -1; } if (ret == 0) { mail_index_set_error(index, - "Unexpected EOF while reading index file"); + "Corrupted index file %s: File too small", + index->filepath); return -1; } map->records = data; map->records_count = hdr.messages_count; - map->hdr_copy = hdr; - map->hdr = &map->hdr_copy; + map->hdr = map->hdr_copy_buf->data; + map->hdr_base = map->hdr_copy_buf->data; return 1; } static int mail_index_read_map_with_retry(struct mail_index *index, struct mail_index_map *map) { - int i, ret; + int i, ret, retry; for (i = 0; i < MAIL_INDEX_ESTALE_RETRY_COUNT; i++) { - ret = mail_index_read_map(index, map); - if (ret != 0) + ret = mail_index_read_map(index, map, &retry); + if (ret != 0 || !retry) return ret; /* ESTALE - reopen index file */ @@ -381,6 +569,9 @@ if (map == NULL) { map = i_new(struct mail_index_map, 1); map->refcount = 1; + map->hdr_copy_buf = + buffer_create_dynamic(default_pool, + sizeof(*map->hdr), (size_t)-1); } else if (MAIL_INDEX_MAP_IS_IN_MEMORY(map)) { if (map->write_to_disk) { /* we have modified this mapping and it's waiting to @@ -432,9 +623,11 @@ mail_index_map_to_memory(struct mail_index_map *map, uint32_t new_record_size) { struct mail_index_map *mem_map; + struct mail_index_header *hdr; + struct mail_index_extra_record_info *einfos; void *src, *dest; size_t size, copy_size; - unsigned int i; + unsigned int i, count; if (MAIL_INDEX_MAP_IS_IN_MEMORY(map)) { map->refcount++; @@ -462,12 +655,50 @@ mem_map->records = buffer_get_modifyable_data(mem_map->buffer, NULL); mem_map->records_count = map->records_count; - mem_map->hdr_copy = *map->hdr; - mem_map->hdr_copy.record_size = new_record_size; - mem_map->hdr = &mem_map->hdr_copy; + mem_map->hdr_copy_buf = buffer_create_dynamic(default_pool, + map->hdr->header_size, + (size_t)-1); + buffer_append(mem_map->hdr_copy_buf, map->hdr, map->hdr->header_size); + + hdr = buffer_get_modifyable_data(mem_map->hdr_copy_buf, NULL); + hdr->record_size = new_record_size; + mem_map->hdr = hdr; + + /* copy extra_infos */ + if (map->extra_infos_id_map != NULL) { + count = map->extra_infos_id_map->used / sizeof(uint32_t); + mail_index_map_create_extra_infos(mem_map, count); + + buffer_append_buf(mem_map->extra_infos, map->extra_infos, + 0, (size_t)-1); + buffer_append_buf(mem_map->extra_infos_id_map, + map->extra_infos_id_map, 0, (size_t)-1); + + /* fix the name pointers to use our own pool */ + einfos = buffer_get_modifyable_data(mem_map->extra_infos, NULL); + for (i = 0; i < count; i++) { + einfos[i].name = p_strdup(mem_map->extra_records_pool, + einfos[i].name); + } + } + return mem_map; } +int mail_index_map_get_extra_info_idx(struct mail_index_map *map, + uint32_t data_id, uint32_t *idx_r) +{ + const uint32_t *id_map; + + if (map->extra_infos_id_map == NULL || + map->extra_infos_id_map->used / sizeof(*id_map) <= data_id) + return 0; + + id_map = map->extra_infos_id_map->data; + *idx_r = id_map[data_id]; + return *idx_r != (uint32_t)-1; +} + static int mail_index_try_open_only(struct mail_index *index) { int i; @@ -550,8 +781,8 @@ return -1; } - index->map->hdr_copy = *hdr; - index->hdr = &index->map->hdr_copy; + buffer_write(index->map->hdr_copy_buf, 0, hdr, hdr_size); + i_assert(index->hdr == index->map->hdr_copy_buf->data); } return 0; @@ -631,8 +862,7 @@ return ret; } -static void mail_index_header_init(struct mail_index *index, - struct mail_index_header *hdr) +static void mail_index_header_init(struct mail_index_header *hdr) { time_t now = time(NULL); @@ -642,7 +872,7 @@ hdr->minor_version = MAIL_INDEX_MINOR_VERSION; hdr->base_header_size = sizeof(*hdr); hdr->header_size = sizeof(*hdr); - hdr->record_size = index->max_record_size; + hdr->record_size = sizeof(struct mail_index_record); hdr->keywords_mask_size = sizeof(keywords_mask_t); #ifndef WORDS_BIGENDIAN @@ -671,7 +901,7 @@ /* doesn't exist, or corrupted */ if ((flags & MAIL_INDEX_OPEN_FLAG_CREATE) == 0) return 0; - mail_index_header_init(index, &hdr); + mail_index_header_init(&hdr); index->hdr = &hdr; } else if (ret < 0) return -1;