mail-index-map.c revision 3e559edb905bb57d1f1fbfda3431b339f45ea791
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync/* Copyright (c) 2003-2007 Dovecot authors, see the included COPYING file */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsyncstatic void mail_index_map_init_extbufs(struct mail_index_map *map,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync ((sizeof(map->extensions) + BUFFER_APPROX_SIZE) * 2)
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* try to use the existing pool's size for initial_count so
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync we don't grow it unneededly */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync size = p_get_max_easy_alloc_size(map->extension_pool);
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (size > EXT_GLOBAL_ALLOC_SIZE + EXT_PER_ALLOC_SIZE) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync p_array_init(&map->extensions, map->extension_pool, initial_count);
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync p_array_init(&map->ext_id_map, map->extension_pool, initial_count);
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsyncbool mail_index_map_lookup_ext(struct mail_index_map *map, const char *name,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync unsigned int i, size;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync for (i = 0; i < size; i++) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsyncunsigned int mail_index_map_ext_hdr_offset(unsigned int name_len)
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync size_t size = sizeof(struct mail_index_ext_header) + name_len;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsyncmail_index_map_register_ext(struct mail_index_map *map, const char *name,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync i_assert(!mail_index_map_lookup_ext(map, name, NULL));
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync ext->index_idx = mail_index_ext_register(map->index, name, hdr_size,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* Update index ext_id -> map ext_id mapping. Fill non-used
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync ext_ids with (uint32_t)-1 */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync while (array_count(&map->ext_id_map) < ext->index_idx)
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync array_idx_set(&map->ext_id_map, ext->index_idx, &idx);
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsyncint mail_index_map_ext_get_next(struct mail_index_map *map,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync unsigned int *offset_p,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync const char **name_r)
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* Extension header contains:
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync - struct mail_index_ext_header
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync - name (not 0-terminated)
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync - 64bit alignment padding
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync - extension header contents
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync - 64bit alignment padding
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (offset + sizeof(*ext_hdr) >= map->hdr.header_size)
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync offset += mail_index_map_ext_hdr_offset(ext_hdr->name_size);
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync *name_r = t_strndup(CONST_PTR_OFFSET(map->hdr_base, name_offset),
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (strcmp(*name_r, str_sanitize(*name_r, -1)) != 0) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* we allow only plain ASCII names, so this extension
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync is most likely broken */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* finally make sure that the hdr_size is small enough.
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync do this last so that we could return a usable name. */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync offset += MAIL_INDEX_HEADER_SIZE_ALIGN(ext_hdr->hdr_size);
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsyncint mail_index_map_ext_hdr_check(const struct mail_index_header *hdr,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if ((ext_hdr->record_size == 0 && ext_hdr->hdr_size == 0) ||
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync (ext_hdr->record_align == 0 && ext_hdr->record_size != 0)) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (ext_hdr->record_offset + ext_hdr->record_size > hdr->record_size) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "outside record size (%u+%u > %u)",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync ((ext_hdr->record_offset % ext_hdr->record_align) != 0 ||
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync (hdr->record_size % ext_hdr->record_align) != 0)) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync *error_r = t_strdup_printf("Record field alignmentation %u "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsyncstatic int mail_index_map_parse_extensions(struct mail_index_map *map)
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* extension headers always start from 64bit offsets, so if base header
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync doesn't happen to be 64bit aligned we'll skip some bytes */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync offset = MAIL_INDEX_HEADER_SIZE_ALIGN(map->hdr.base_header_size);
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (offset >= map->hdr.header_size && map->extension_pool == NULL) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* nothing to do, skip allocatations and all */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync for (i = 0; i < old_count; i++)
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "Header extension #%d (%s) goes outside header",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (mail_index_map_ext_hdr_check(&map->hdr, ext_hdr,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "Broken extension #%d (%s): %s",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "Duplicate header extension %s",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsyncint mail_index_map_parse_keywords(struct mail_index_map *map)
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync const struct mail_index_keyword_header_rec *kw_rec;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync const char *name;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (!mail_index_map_lookup_ext(map, "keywords", &idx)) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* Extension header contains:
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync - struct mail_index_keyword_header
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync - struct mail_index_keyword_header_rec * keywords_count
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync - const char names[] * keywords_count
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync kw_hdr = CONST_PTR_OFFSET(map->hdr_base, ext->hdr_offset);
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync name = (const char *)(kw_rec + kw_hdr->keywords_count);
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync old_count = !array_is_created(&map->keyword_idx_map) ? 0 :
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* Keywords can only be added into same mapping. Removing requires a
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync new mapping (recreating the index file) */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* nothing changed */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* make sure the header is valid */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "Keywords removed unexpectedly",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if ((size_t)(name - (const char *)kw_hdr) > ext->hdr_size) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "keywords_count larger than header size",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync name_area_end_offset = (const char *)kw_hdr + ext->hdr_size - name;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (kw_rec[i].name_offset > name_area_end_offset) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "name_offset points outside allocated header",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "Keyword header doesn't end with NUL",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* create file -> index mapping */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync i_array_init(&map->keyword_idx_map, kw_hdr->keywords_count);
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* Check that existing headers are still the same. It's behind DEBUG
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync since it's pretty useless waste of CPU normally. */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync for (i = 0; i < array_count(&map->keyword_idx_map); i++) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync const char *keyword = name + kw_rec[i].name_offset;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync const unsigned int *old_idx;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync unsigned int idx;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (!mail_index_keyword_lookup(index, keyword, &idx) ||
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "Keywords changed unexpectedly",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* Register the newly seen keywords */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync const char *keyword = name + kw_rec[i].name_offset;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync unsigned int idx;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "Empty keyword name in header",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_keyword_lookup_or_create(index, keyword, &idx);
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsyncstatic bool mail_index_check_header_compat(struct mail_index *index,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync enum mail_index_header_compat_flags compat_flags = 0;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (hdr->major_version != MAIL_INDEX_MAJOR_VERSION) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* major version change - handle silently(?) */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if ((hdr->flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* we've already complained about it */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* architecture change */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Rebuilding index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "CPU architecture changed",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (hdr->base_header_size < MAIL_INDEX_HEADER_MIN_SIZE ||
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "Corrupted header sizes (base %u, full %u)",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "indexid changed: %u -> %u",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsyncint mail_index_map_check_header(struct mail_index_map *map)
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (!mail_index_check_header_compat(index, hdr, (uoff_t)-1))
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* following some extra checks that only take a bit of CPU */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (hdr->record_size < sizeof(struct mail_index_record)) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync sizeof(struct mail_index_record));
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (hdr->messages_count > map->rec_map->records_count)
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (hdr->seen_messages_count > hdr->messages_count ||
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (hdr->first_recent_uid == 0 && hdr->minor_version == 0) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* upgrade silently from v1.0 */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* last message's UID must be smaller than next_uid.
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync also make sure it's not zero. */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync rec = MAIL_INDEX_MAP_IDX(map, hdr->messages_count-1);
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsyncstatic void mail_index_map_copy_hdr(struct mail_index_map *map,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* header smaller than ours, make a copy so our newer headers
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync won't have garbage in them */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* FIXME: backwards compatibility, remove later. In case this index is
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync accessed with Dovecot v1.0, avoid recent message counter errors. */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsyncstatic int mail_index_mmap(struct mail_index_map *map, uoff_t file_size)
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync struct mail_index_record_map *rec_map = map->rec_map;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* too large file to map into memory */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Index file too large: %s",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync rec_map->mmap_base = mmap(NULL, file_size, PROT_READ | PROT_WRITE,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync offsetof(struct mail_index_header, major_version) &&
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* major version change - handle silently */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (rec_map->mmap_size < MAIL_INDEX_HEADER_MIN_SIZE) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (!mail_index_check_header_compat(index, hdr, rec_map->mmap_size)) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* Can't use this file */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "messages_count too large (%u > %u)",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync rec_map->records = PTR_OFFSET(rec_map->mmap_base, map->hdr.header_size);
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsyncstatic int mail_index_read_header(struct mail_index *index,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* try to read the whole header, but it's not necessarily an error to
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync read less since the older versions of the index format could be
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync smaller. Request reading up to buf_size, but accept if we only got
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync the header. */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync } while (ret > 0 && pos < sizeof(struct mail_index_header));
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync const void *buf;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync ret = mail_index_read_header(index, read_buf, sizeof(read_buf), &pos);
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (pos > (ssize_t)offsetof(struct mail_index_header, major_version) &&
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* major version change - handle silently */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (ret >= 0 && pos >= MAIL_INDEX_HEADER_MIN_SIZE &&
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (!mail_index_check_header_compat(index, hdr, file_size)) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* Can't use this file */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* place the base header into memory. */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* @UNSAFE: read the rest of the header into memory */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync data = buffer_append_space_unsafe(map->hdr_copy_buf,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* header read, read the records now. */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync records_size = (size_t)hdr->messages_count * hdr->record_size;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync records_size / hdr->record_size != hdr->messages_count)) {
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync records_size = (size_t)records_count * hdr->record_size;
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_error(index, "Corrupted index file %s: "
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync "messages_count too large (%u > %u)",
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* @UNSAFE */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync data = buffer_append_space_unsafe(map->rec_map->buffer,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync ret = pread_full(index->fd, data, records_size - extra,
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync /* a new index file was renamed over this one. */
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync mail_index_set_syscall_error(index, "pread_full()");
a734c64bff58bda2fa48c2795453e092167b0ff7vboxsync if (ret == 0) {
unsigned int *lock_id)
unsigned int i, count;
int ret;
for (i = 0; i < count; i++)
ret = 0;
*lock_id = 0;
if (ret <= 0) {
if (ret == 0) {
return ret;
#ifndef WORDS_BIGENDIAN
unsigned int lock_id;
bool use_mmap;
if (ret <= 0) {
if (ret < 0)
if (use_mmap) {
if (ret > 0) {
ret = 0;
ret = 0;
if (ret <= 0) {
return ret;
int ret;
ret = 0;
if (ret == 0) {
return ret;
unsigned int i, count;
for (i = 0; i < count; i++) {
i_unreached();
unsigned int record_size)
static struct mail_index_record_map *
return rec_map;
unsigned int i, count;
for (i = 0; i < count; i++) {
return mem_map;
return FALSE;