mail-index-map.c revision c69a177207ed18d0f0210347430a60957136bd6c
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2003-2017 Dovecot authors, see the included COPYING file */
5806683c1c3f5b1997e92a023c0fe39912d4df5dTimo Sirainenvoid mail_index_map_init_extbufs(struct mail_index_map *map,
8e361d2906b0e44f7175a20981f8d2280645b58bTimo Sirainen ((sizeof(map->extensions) + sizeof(buffer_t)) * 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,
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen *idx_r = array_foreach_idx(&map->extensions, ext);
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,
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen uint32_t idx, ext_map_idx, empty_idx = (uint32_t)-1;
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen i_assert(!mail_index_map_lookup_ext(map, name, &ext_map_idx));
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen ext->name = p_strdup(map->extension_pool, name);
2c488bf1ca9194cf36597959f1316b7e12102fd9Timo Sirainen ext->hdr_offset = ext_offset == (uint32_t)-1 ? (uint32_t)-1 :
2c488bf1ca9194cf36597959f1316b7e12102fd9Timo Sirainen ext_offset + mail_index_map_ext_hdr_offset(strlen(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);
eb958701f57066bce9cc8bbf9df73616bd322d09Timo Sirainenmail_index_map_ext_hdr_check_record(const struct mail_index_header *hdr,
eb958701f57066bce9cc8bbf9df73616bd322d09Timo Sirainen const char **error_r)
4ad29660779873d7955151eb4568d132a32e9d9cTimo Sirainen /* until we get 128 bit CPUs having a larger alignment is pointless */
4ad29660779873d7955151eb4568d132a32e9d9cTimo Sirainen if (ext_hdr->record_align > sizeof(uint64_t)) {
4ad29660779873d7955151eb4568d132a32e9d9cTimo Sirainen /* a large record size is most likely a bug somewhere. the maximum
4ad29660779873d7955151eb4568d132a32e9d9cTimo Sirainen record size is limited to 64k anyway, so try to fail earlier. */
4a01681e0ed9a7b3f7a105dd2fcd2511e7160e99Timo Sirainen /* if we get here from extension introduction, record_offset=0
4a01681e0ed9a7b3f7a105dd2fcd2511e7160e99Timo Sirainen and hdr->record_size hasn't been updated yet */
eb958701f57066bce9cc8bbf9df73616bd322d09Timo 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)",
eb958701f57066bce9cc8bbf9df73616bd322d09Timo Sirainen if ((ext_hdr->record_offset % ext_hdr->record_align) != 0) {
9573f4283f9118315b0865998d43223fedee0246Timo Sirainen *error_r = t_strdup_printf("Record field alignment %u "
eb958701f57066bce9cc8bbf9df73616bd322d09Timo Sirainen if ((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",
eb958701f57066bce9cc8bbf9df73616bd322d09Timo Sirainenint mail_index_map_ext_hdr_check(const struct mail_index_header *hdr,
eb958701f57066bce9cc8bbf9df73616bd322d09Timo Sirainen if (ext_hdr->record_size == 0 && ext_hdr->hdr_size == 0) {
eb958701f57066bce9cc8bbf9df73616bd322d09Timo Sirainen if (mail_index_map_ext_hdr_check_record(hdr, ext_hdr,
ed50658501d9ae8c85f6264831056b1debed11c3Timo Sirainen if (ext_hdr->hdr_size > MAIL_INDEX_EXT_HEADER_MAX_SIZE) {
ed50658501d9ae8c85f6264831056b1debed11c3Timo Sirainen *error_r = t_strdup_printf("Headersize too large (%u)",
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 */
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainenstatic void mail_index_record_map_free(struct mail_index_map *map,
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)
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen idx = array_foreach_idx(&map->rec_map->maps, maps);
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo 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,
749097cae4e02bdeccb8b09f7dda6e2da6812ee1Timo Sirainen /* +1% so we have a bit of space to grow. useful for huge mailboxes. */
749097cae4e02bdeccb8b09f7dda6e2da6812ee1Timo Sirainen dest->buffer = buffer_create_dynamic(default_pool,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen buffer_append(dest->buffer, src->records, size);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen dest->records = buffer_get_modifiable_data(dest->buffer, NULL);
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)
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen unsigned int 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);
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 */
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen array_foreach_modifiable(&mem_map->extensions, ext) {
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen i_assert(ext->record_offset + ext->record_size <=
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,
90a989e4f8bcb4fb86af32cbae577402e3f92b1aTimo Sirainen new_map->modseq = 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;
1df526903ed039e8ff966a223c43b8d04eddf3c7Phil Carmody rec = MAIL_INDEX_REC_AT_SEQ(map, new_map->records_count);
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,
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen if (munmap(new_map->mmap_base, new_map->mmap_size) < 0)
3697080532ccd9f51fac108be6079b616c7a2ddfTimo Sirainen mail_index_set_syscall_error(map->index, "munmap()");
12053b7b4b57dbd2790057426d1633988eedad56Timo Sirainenbool mail_index_map_get_ext_idx(struct mail_index_map *map,
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainenstatic uint32_t mail_index_bsearch_uid(struct mail_index_map *map,
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainen const struct mail_index_record *rec_base, *rec;
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainen i_assert(map->hdr.messages_count <= map->rec_map->records_count);
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainen right_idx = I_MIN(map->hdr.messages_count, uid);
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainen rec = CONST_PTR_OFFSET(rec_base, idx * record_size);
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainen rec = CONST_PTR_OFFSET(rec_base, idx * record_size);
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainen /* we want uid or larger */
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainen (idx == map->hdr.messages_count-1 ? 0 : idx+2);
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainen /* we want uid or smaller */
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainenvoid mail_index_map_lookup_seq_range(struct mail_index_map *map,
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainen *first_seq_r = mail_index_bsearch_uid(map, first_uid, 0, 1);
1df526903ed039e8ff966a223c43b8d04eddf3c7Phil Carmody MAIL_INDEX_REC_AT_SEQ(map, *first_seq_r)->uid > last_uid) {
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainen /* we want the last message */
c24c0f0a208e5ffc35dc8be19a9b504a5326467aTimo Sirainen /* optimization - binary lookup only from right side: */