mail-index-map.c revision 7de1c472fd23ddac6b4dc5cbeee6fa6a8418b071
76b43e4417bab52e913da39b5f5bc2a130d3f149Timo Sirainen/* Copyright (c) 2003-2008 Dovecot authors, see the included COPYING file */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic void mail_index_map_init_extbufs(struct mail_index_map *map,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen ((sizeof(map->extensions) + BUFFER_APPROX_SIZE) * 2)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen sizeof(struct mail_index_ext) + sizeof(uint32_t))
221351ed85c839e0b03d82c47654c3d17202e3dbTimo Sirainen pool_alloconly_create(MEMPOOL_GROWING"map extensions",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* try to use the existing pool's size for initial_count so
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen we don't grow it unneededly */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen size = p_get_max_easy_alloc_size(map->extension_pool);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (size > EXT_GLOBAL_ALLOC_SIZE + EXT_PER_ALLOC_SIZE) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen initial_count = (size - EXT_GLOBAL_ALLOC_SIZE) /
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen p_array_init(&map->extensions, map->extension_pool, initial_count);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen p_array_init(&map->ext_id_map, map->extension_pool, initial_count);
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainenbool mail_index_map_lookup_ext(struct mail_index_map *map, const char *name,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen unsigned int i, size;
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen extensions = array_get(&map->extensions, &size);
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen for (i = 0; i < size; i++) {
8ababf3e7b15f793370d1dedf85825d38b42633fTimo Sirainenunsigned int mail_index_map_ext_hdr_offset(unsigned int name_len)
cfa9359fbd6a967ccdcd553c5e483a093885ab6fTimo Sirainen size_t size = sizeof(struct mail_index_ext_header) + name_len;
3675a7e9bd3775ba13fe8bc93915902513a0f1a4Timo Sirainenmail_index_map_register_ext(struct mail_index_map *map,
7ded22760598b78ee29f9418eacc0abe3fb51055Timo Sirainen i_assert(!mail_index_map_lookup_ext(map, name, NULL));
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen ext->name = p_strdup(map->extension_pool, name);
3675a7e9bd3775ba13fe8bc93915902513a0f1a4Timo Sirainen ext->index_idx = mail_index_ext_register(map->index, name,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* Update index ext_id -> map ext_id mapping. Fill non-used
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen ext_ids with (uint32_t)-1 */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen while (array_count(&map->ext_id_map) < ext->index_idx)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen array_append(&map->ext_id_map, &empty_idx, 1);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen array_idx_set(&map->ext_id_map, ext->index_idx, &idx);
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainenint mail_index_map_ext_get_next(struct mail_index_map *map,
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen unsigned int *offset_p,
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen const struct mail_index_ext_header **ext_hdr_r,
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen const char **name_r)
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen /* Extension header contains:
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen - struct mail_index_ext_header
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen - name (not 0-terminated)
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen - 64bit alignment padding
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen - extension header contents
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen - 64bit alignment padding
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen ext_hdr = CONST_PTR_OFFSET(map->hdr_base, offset);
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen if (offset + sizeof(*ext_hdr) >= map->hdr.header_size)
8ababf3e7b15f793370d1dedf85825d38b42633fTimo Sirainen offset += mail_index_map_ext_hdr_offset(ext_hdr->name_size);
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen *name_r = t_strndup(CONST_PTR_OFFSET(map->hdr_base, name_offset),
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen if (strcmp(*name_r, str_sanitize(*name_r, -1)) != 0) {
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen /* we allow only plain ASCII names, so this extension
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen is most likely broken */
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen /* finally make sure that the hdr_size is small enough.
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen do this last so that we could return a usable name. */
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen offset += MAIL_INDEX_HEADER_SIZE_ALIGN(ext_hdr->hdr_size);
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainenint mail_index_map_ext_hdr_check(const struct mail_index_header *hdr,
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen if ((ext_hdr->record_size == 0 && ext_hdr->hdr_size == 0) ||
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen (ext_hdr->record_align == 0 && ext_hdr->record_size != 0)) {
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen if (ext_hdr->record_offset + ext_hdr->record_size > hdr->record_size) {
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen *error_r = t_strdup_printf("Record field points "
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen "outside record size (%u+%u > %u)",
6c9699d37fbe4d8af3682ee7f341ede8d54faa87Timo Sirainen (ext_hdr->record_offset % ext_hdr->record_align) != 0) {
9573f4283f9118315b0865998d43223fedee0246Timo Sirainen *error_r = t_strdup_printf("Record field alignment %u "
6c9699d37fbe4d8af3682ee7f341ede8d54faa87Timo Sirainen /* if we get here from extension introduction, record_offset=0 and
6c9699d37fbe4d8af3682ee7f341ede8d54faa87Timo Sirainen hdr->record_size hasn't been updated yet */
6c9699d37fbe4d8af3682ee7f341ede8d54faa87Timo Sirainen (hdr->record_size % ext_hdr->record_align) != 0) {
6c9699d37fbe4d8af3682ee7f341ede8d54faa87Timo Sirainen *error_r = t_strdup_printf("Record size not aligned by %u "
6c9699d37fbe4d8af3682ee7f341ede8d54faa87Timo Sirainen "as required by extension",
ed50658501d9ae8c85f6264831056b1debed11c3Timo Sirainen if (ext_hdr->hdr_size > MAIL_INDEX_EXT_HEADER_MAX_SIZE) {
ed50658501d9ae8c85f6264831056b1debed11c3Timo Sirainen *error_r = t_strdup_printf("Headersize too large (%u)",
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainenstatic int mail_index_map_parse_extensions(struct mail_index_map *map)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* extension headers always start from 64bit offsets, so if base header
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen doesn't happen to be 64bit aligned we'll skip some bytes */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen offset = MAIL_INDEX_HEADER_SIZE_ALIGN(map->hdr.base_header_size);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (offset >= map->hdr.header_size && map->extension_pool == NULL) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* nothing to do, skip allocatations and all */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_map_init_extbufs(map, old_count + 5);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen for (i = 0; i < old_count; i++)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen for (i = 0; offset < map->hdr.header_size; i++) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen "Header extension #%d (%s) goes outside header",
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen if (mail_index_map_ext_hdr_check(&map->hdr, ext_hdr,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen "Broken extension #%d (%s): %s",
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen if (mail_index_map_lookup_ext(map, name, NULL)) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen "Duplicate header extension %s",
3675a7e9bd3775ba13fe8bc93915902513a0f1a4Timo Sirainen mail_index_map_register_ext(map, name, ext_offset, ext_hdr);
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainenint mail_index_map_parse_keywords(struct mail_index_map *map)
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen const struct mail_index_keyword_header *kw_hdr;
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen const struct mail_index_keyword_header_rec *kw_rec;
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen unsigned int i, name_area_end_offset, old_count;
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen if (!mail_index_map_lookup_ext(map, "keywords", &idx)) {
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen /* Extension header contains:
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen - struct mail_index_keyword_header
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen - struct mail_index_keyword_header_rec * keywords_count
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen - const char names[] * keywords_count
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen i_assert(ext->hdr_offset < map->hdr.header_size);
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen kw_hdr = CONST_PTR_OFFSET(map->hdr_base, ext->hdr_offset);
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen name = (const char *)(kw_rec + kw_hdr->keywords_count);
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen old_count = !array_is_created(&map->keyword_idx_map) ? 0 :
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen /* Keywords can only be added into same mapping. Removing requires a
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen new mapping (recreating the index file) */
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen /* nothing changed */
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen /* make sure the header is valid */
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen "Keywords removed unexpectedly",
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen if ((size_t)(name - (const char *)kw_hdr) > ext->hdr_size) {
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen "keywords_count larger than header size",
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen name_area_end_offset = (const char *)kw_hdr + ext->hdr_size - name;
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen for (i = 0; i < kw_hdr->keywords_count; i++) {
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen if (kw_rec[i].name_offset > name_area_end_offset) {
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen "name_offset points outside allocated header",
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen "Keyword header doesn't end with NUL",
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen /* create file -> index mapping */
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen i_array_init(&map->keyword_idx_map, kw_hdr->keywords_count);
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen /* Check that existing headers are still the same. It's behind DEBUG
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen since it's pretty useless waste of CPU normally. */
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen for (i = 0; i < array_count(&map->keyword_idx_map); i++) {
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen const char *keyword = name + kw_rec[i].name_offset;
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen const unsigned int *old_idx;
b8d232d88018c5cafd2f3be5a181d318137a45f2Timo Sirainen unsigned int kw_idx;
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen old_idx = array_idx(&map->keyword_idx_map, i);
b8d232d88018c5cafd2f3be5a181d318137a45f2Timo Sirainen if (!mail_index_keyword_lookup(index, keyword, &kw_idx) ||
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen "Keywords changed unexpectedly",
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen /* Register the newly seen keywords */
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen const char *keyword = name + kw_rec[i].name_offset;
b8d232d88018c5cafd2f3be5a181d318137a45f2Timo Sirainen unsigned int kw_idx;
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
2bd2ca14d18819b77201ab2b46910d9ae6858abeTimo Sirainen "Empty keyword name in header",
b8d232d88018c5cafd2f3be5a181d318137a45f2Timo Sirainen mail_index_keyword_lookup_or_create(index, keyword, &kw_idx);
b8d232d88018c5cafd2f3be5a181d318137a45f2Timo Sirainen array_append(&map->keyword_idx_map, &kw_idx, 1);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic bool mail_index_check_header_compat(struct mail_index *index,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen enum mail_index_header_compat_flags compat_flags = 0;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen compat_flags |= MAIL_INDEX_COMPAT_LITTLE_ENDIAN;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (hdr->major_version != MAIL_INDEX_MAJOR_VERSION) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* major version change - handle silently(?) */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if ((hdr->flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* we've already complained about it */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* architecture change */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen mail_index_set_error(index, "Rebuilding index file %s: "
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen "CPU architecture changed",
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (hdr->base_header_size < MAIL_INDEX_HEADER_MIN_SIZE ||
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen "Corrupted header sizes (base %u, full %u)",
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen "indexid changed: %u -> %u",
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen mail_transaction_log_indexid_changed(index->log);
50031a6b36a6051512bd18f39e4bbabe54acf565Timo Sirainenstatic void mail_index_map_clear_recent_flags(struct mail_index_map *map)
50031a6b36a6051512bd18f39e4bbabe54acf565Timo Sirainen unsigned int i;
50031a6b36a6051512bd18f39e4bbabe54acf565Timo Sirainen for (i = 0; i < map->hdr.messages_count; i++) {
f7656d7bc15510a4259ed74ddda3c560de8a51c1Timo Sirainenint mail_index_map_check_header(struct mail_index_map *map)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen const struct mail_index_header *hdr = &map->hdr;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (!mail_index_check_header_compat(index, hdr, (uoff_t)-1))
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* following some extra checks that only take a bit of CPU */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (hdr->record_size < sizeof(struct mail_index_record)) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen sizeof(struct mail_index_record));
7eff76882487ef39803446f75a709a97a3cafc53Timo Sirainen if (hdr->uid_validity == 0 && hdr->next_uid != 1)
2aa3ac8bfb8fea49fd0ab6d56afcb3af3375d22cTimo Sirainen if (hdr->messages_count > map->rec_map->records_count)
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen if (hdr->seen_messages_count > hdr->messages_count ||
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen hdr->deleted_messages_count > hdr->messages_count)
3e559edb905bb57d1f1fbfda3431b339f45ea791Timo Sirainen /* upgrade silently from v1.0 */
608ae99c5b03989df263d72e49aa83e1f9d8a50eTimo Sirainen map->hdr.unused_old_recent_messages_count = 0;
50031a6b36a6051512bd18f39e4bbabe54acf565Timo Sirainen /* fall through */
50031a6b36a6051512bd18f39e4bbabe54acf565Timo Sirainen /* pre-v1.1.rc6: make sure the \Recent flags are gone */
50031a6b36a6051512bd18f39e4bbabe54acf565Timo Sirainen map->hdr.minor_version = MAIL_INDEX_MINOR_VERSION;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen hdr->first_unseen_uid_lowwater > hdr->next_uid ||
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen hdr->first_deleted_uid_lowwater > hdr->next_uid)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* last message's UID must be smaller than next_uid.
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen also make sure it's not zero. */
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen rec = MAIL_INDEX_MAP_IDX(map, hdr->messages_count-1);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (rec->uid == 0 || rec->uid >= hdr->next_uid)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainenstatic void mail_index_map_copy_hdr(struct mail_index_map *map,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (hdr->base_header_size < sizeof(map->hdr)) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* header smaller than ours, make a copy so our newer headers
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen won't have garbage in them */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen memcpy(&map->hdr, hdr, hdr->base_header_size);
91add6e26ed2b91e15276c96ffe02d9f71476d71Timo Sirainen /* FIXME: backwards compatibility, remove later. In case this index is
91add6e26ed2b91e15276c96ffe02d9f71476d71Timo Sirainen accessed with Dovecot v1.0, avoid recent message counter errors. */
91add6e26ed2b91e15276c96ffe02d9f71476d71Timo Sirainen map->hdr.unused_old_recent_messages_count = 0;
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainenstatic int mail_index_mmap(struct mail_index_map *map, uoff_t file_size)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen struct mail_index_record_map *rec_map = map->rec_map;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* too large file to map into memory */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen mail_index_set_error(index, "Index file too large: %s",
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen rec_map->mmap_base = mmap(NULL, file_size, PROT_READ | PROT_WRITE,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_syscall_error(index, "mmap()");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen offsetof(struct mail_index_header, major_version) &&
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen hdr->major_version != MAIL_INDEX_MAJOR_VERSION) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* major version change - handle silently */
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (rec_map->mmap_size < MAIL_INDEX_HEADER_MIN_SIZE) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (!mail_index_check_header_compat(index, hdr, rec_map->mmap_size)) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* Can't use this file */
2aa3ac8bfb8fea49fd0ab6d56afcb3af3375d22cTimo Sirainen if (rec_map->mmap_used_size <= rec_map->mmap_size)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "messages_count too large (%u > %u)",
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen rec_map->records = PTR_OFFSET(rec_map->mmap_base, map->hdr.header_size);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic int mail_index_read_header(struct mail_index *index,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen memset(buf, 0, sizeof(struct mail_index_header));
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* try to read the whole header, but it's not necessarily an error to
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen read less since the older versions of the index format could be
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen smaller. Request reading up to buf_size, but accept if we only got
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen the header. */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen } while (ret > 0 && pos < sizeof(struct mail_index_header));
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainenmail_index_try_read_map(struct mail_index_map *map,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen uoff_t file_size, bool *retry_r, bool try_retry)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen const void *buf;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen size_t pos, records_size, initial_buf_pos = 0;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen ret = mail_index_read_header(index, read_buf, sizeof(read_buf), &pos);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (pos > (ssize_t)offsetof(struct mail_index_header, major_version) &&
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen hdr->major_version != MAIL_INDEX_MAJOR_VERSION) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* major version change - handle silently */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen if (ret >= 0 && pos >= MAIL_INDEX_HEADER_MIN_SIZE &&
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (!mail_index_check_header_compat(index, hdr, file_size)) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* Can't use this file */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* place the base header into memory. */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* @UNSAFE: read the rest of the header into memory */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen data = buffer_append_space_unsafe(map->hdr_copy_buf,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* header read, read the records now. */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen records_size = (size_t)hdr->messages_count * hdr->record_size;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (file_size - hdr->header_size < records_size ||
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen records_size / hdr->record_size != hdr->messages_count)) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen records_count = (file_size - hdr->header_size) /
2aa3ac8bfb8fea49fd0ab6d56afcb3af3375d22cTimo Sirainen records_size = (size_t)records_count * hdr->record_size;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "messages_count too large (%u > %u)",
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* @UNSAFE */
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen buffer_set_used_size(map->rec_map->buffer, 0);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen data = buffer_append_space_unsafe(map->rec_map->buffer,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen ret = pread_full(index->fd, data, records_size - extra,
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* a new index file was renamed over this one. */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_syscall_error(index, "pread_full()");
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen "Corrupted index file %s: File too small",
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen buffer_get_modifiable_data(map->rec_map->buffer, NULL);
c2feb7d13482d0f60691cd71d06d42a80df99397Timo Sirainenstatic int mail_index_read_map(struct mail_index_map *map, uoff_t file_size,
c2feb7d13482d0f60691cd71d06d42a80df99397Timo Sirainen unsigned int *lock_id)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_sync_lost_handler_t *const *handlers;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen unsigned int i, count;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* notify all "sync lost" handlers */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen handlers = array_get(&index->sync_lost_handlers, &count);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen for (i = 0; i < count; i++)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen for (i = 0;; i++) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen try_retry = i < MAIL_INDEX_ESTALE_RETRY_COUNT;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* fstat() below failed */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* ESTALE - reopen index file */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* the file was lost */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_set_syscall_error(index, "open()");
c2feb7d13482d0f60691cd71d06d42a80df99397Timo Sirainen if (mail_index_lock_shared(index, lock_id) < 0)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen mail_index_set_syscall_error(index, "fstat()");
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstatic void mail_index_header_init(struct mail_index *index,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen i_assert((sizeof(*hdr) % sizeof(uint64_t)) == 0);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen hdr->major_version = MAIL_INDEX_MAJOR_VERSION;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen hdr->minor_version = MAIL_INDEX_MINOR_VERSION;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen hdr->record_size = sizeof(struct mail_index_record);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen hdr->compat_flags |= MAIL_INDEX_COMPAT_LITTLE_ENDIAN;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstruct mail_index_map *mail_index_map_alloc(struct mail_index *index)
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* a bit kludgy way to do this, but it initializes everything
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen nicely and correctly */
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen/* returns -1 = error, 0 = index files are unusable,
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen 1 = index files are usable or at least repairable */
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainenstatic int mail_index_map_latest_file(struct mail_index *index)
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen unsigned int lock_id;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* the index file is lost/broken. let's hope that we can
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen build it from the transaction log. */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* the index file is still open, lock it */
14c474d9f4591c397ed0b5206af6537c7b52c924Timo Sirainen if (mail_index_lock_shared(index, &lock_id) < 0)
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen nfs_flush_attr_cache_fd_locked(index->filepath, index->fd);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen mail_index_set_syscall_error(index, "fstat()");
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* mmaping seems to be slower than just reading the file, so even if
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen mmap isn't disabled don't use it unless the file is large enough */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen use_mmap = !index->mmap_disable && file_size != (uoff_t)-1 &&
c2feb7d13482d0f60691cd71d06d42a80df99397Timo Sirainen ret = mail_index_read_map(new_map, file_size, &lock_id);
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen /* the index files are unusable */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* make sure the header is ok before using this mapping */
746d35bf3dba3ae5ddbcecb9732f60d5e9de77efTimo Sirainen if (mail_index_map_parse_extensions(new_map) < 0)
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen else if (mail_index_map_parse_keywords(new_map) < 0)
1fd0d511885c30028aba388588151acf4ee85e75Timo Sirainen /* fsck and try again */
180503c08ebd58174c4f7f1652d716266877e2a3Timo Sirainen /* fsck replaced the map */
8e57335924f5ff57cbd1929ec99764dc267c3312Timo Sirainen index->last_read_log_file_seq = new_map->hdr.log_file_seq;
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* first try updating the existing mapping from transaction log. */
38efb29cb9453917c1b642095a37d0e901f56eeeTimo Sirainen if (index->map->hdr.indexid != 0 && index->indexid != 0) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* we're not creating the index, or opening transaction log.
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen sync this as a view from transaction log. */
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainen ret = mail_index_sync_map(&index->map, type, FALSE);
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen /* try to open and read the latest index. if it fails, we'll
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen fallback to updating the existing mapping from transaction
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen logs (which we'll also do even if the reopening succeeds).
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen if index files are unusable (e.g. major version change)
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen don't even try to use the transaction log. */
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen /* make sure we don't try to open the file again */
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen if (unlink(index->filepath) < 0 && errno != ENOENT)
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen mail_index_set_syscall_error(index, "unlink()");
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen /* if we're creating the index file, we don't have any
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen if (index->log->head != NULL && index->indexid != 0) {
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen /* and update the map with the latest changes
9a0c37cb8588cd287106374eda22a7ea3e2742b2Timo Sirainen from transaction log */
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainenstatic void mail_index_record_map_free(struct mail_index_map *map,
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen mail_index_unlock(map->index, &rec_map->lock_id);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (munmap(rec_map->mmap_base, rec_map->mmap_size) < 0)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen mail_index_set_syscall_error(map->index, "munmap()");
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainenstatic void mail_index_record_map_unlink(struct mail_index_map *map)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen unsigned int i, count;
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen maps = array_get(&map->rec_map->maps, &count);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen for (i = 0; i < count; i++) {
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen mail_index_record_map_free(map, map->rec_map);
b0be0bead3d6963149f7f2a9504b8ab5aced9af5Timo Sirainenvoid mail_index_unmap(struct mail_index_map **_map)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainenstatic void mail_index_map_copy_records(struct mail_index_record_map *dest,
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen dest->buffer = buffer_create_dynamic(default_pool, I_MIN(size, 1024));
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen buffer_append(dest->buffer, src->records, size);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen dest->records = buffer_get_modifiable_data(dest->buffer, NULL);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen /* if the map is ever written back to disk, we need to keep track of
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen what has changed. */
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainenstatic void mail_index_map_copy_header(struct mail_index_map *dest,
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen /* use src->hdr copy directly, because if we got here
19779377be72c9fe8365bb9ba7a2e0d06dc99c3bTimo Sirainen from syncing it has the latest changes. */
19779377be72c9fe8365bb9ba7a2e0d06dc99c3bTimo Sirainen I_MIN(sizeof(dest->hdr), src->hdr.base_header_size));
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen buffer_write(dest->hdr_copy_buf, src->hdr.base_header_size,
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen src->hdr.header_size - src->hdr.base_header_size);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen dest->hdr_base = buffer_get_modifiable_data(dest->hdr_copy_buf, NULL);
5787e39e2be32f657b8c98fee8bac794aa852cf8Timo Sirainen i_assert(dest->hdr_copy_buf->used == dest->hdr.header_size);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainenmail_index_record_map_alloc(struct mail_index_map *map)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen rec_map = i_new(struct mail_index_record_map, 1);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenstruct mail_index_map *mail_index_map_clone(const struct mail_index_map *map)
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen unsigned int i, count;
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen mem_map->rec_map = mail_index_record_map_alloc(mem_map);
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainen array_append(&mem_map->rec_map->maps, &mem_map, 1);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen mem_map->write_base_header = map->write_base_header;
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen mem_map->write_ext_header = map->write_ext_header;
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* copy extensions */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen mail_index_map_init_extbufs(mem_map, count + 2);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen array_append_array(&mem_map->extensions, &map->extensions);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen array_append_array(&mem_map->ext_id_map, &map->ext_id_map);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen /* fix the name pointers to use our own pool */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen extensions = array_get_modifiable(&mem_map->extensions, &count);
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen for (i = 0; i < count; i++) {
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen extensions[i].name = p_strdup(mem_map->extension_pool,
d051664df497582e1eb75a9f238d04b65e858db8Timo Sirainen /* copy keyword map */
d051664df497582e1eb75a9f238d04b65e858db8Timo Sirainen if (array_is_created(&map->keyword_idx_map)) {
a28a6267f48971117dec958b160deefd14ebb7a6Timo Sirainenvoid mail_index_record_map_move_to_private(struct mail_index_map *map)
d0ab5936be1cd971007fc2241e0be120c442cb84Timo Sirainen mail_index_map_copy_records(new_map, map->rec_map,
7de1c472fd23ddac6b4dc5cbeee6fa6a8418b071Timo Sirainen mail_index_map_modseq_clone(map->rec_map->modseq);
d0ab5936be1cd971007fc2241e0be120c442cb84Timo Sirainen if (new_map->records_count != map->hdr.messages_count) {
d0ab5936be1cd971007fc2241e0be120c442cb84Timo Sirainen new_map->records_count = map->hdr.messages_count;
d0ab5936be1cd971007fc2241e0be120c442cb84Timo Sirainen rec = MAIL_INDEX_MAP_IDX(map, new_map->records_count-1);
d0ab5936be1cd971007fc2241e0be120c442cb84Timo Sirainen buffer_set_used_size(new_map->buffer, new_map->records_count *
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenvoid mail_index_map_move_to_memory(struct mail_index_map *map)
7de1c472fd23ddac6b4dc5cbeee6fa6a8418b071Timo Sirainen new_map->modseq = map->rec_map->modseq == NULL ? NULL :
7de1c472fd23ddac6b4dc5cbeee6fa6a8418b071Timo Sirainen mail_index_map_modseq_clone(map->rec_map->modseq);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen mail_index_map_copy_records(new_map, map->rec_map,
88187ee880b4829443e0d55ea7d145d9d5880217Timo Sirainen mail_index_unlock(map->index, &new_map->lock_id);
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (munmap(new_map->mmap_base, new_map->mmap_size) < 0)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen mail_index_set_syscall_error(map->index, "munmap()");