mail-cache-fields.c revision 9dba6a3234ffd20d3c0260897b845b58399067f9
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen#define CACHE_FIELD_IS_NEWLY_WANTED(cache, field_idx) \
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ((cache)->field_file_map[field_idx] == (uint32_t)-1 && \
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenstatic bool field_has_fixed_size(enum mail_cache_field_type type)
862ec874f9373e3e499e237d3b9f71fdf1413feeTimo Sirainenstatic bool field_decision_is_valid(enum mail_cache_decision_type type)
9a48c2243fe98ca8393be7908f84d20c634bcdf9Timo Sirainenstatic int field_type_verify(struct mail_cache *cache, unsigned int idx,
9a48c2243fe98ca8393be7908f84d20c634bcdf9Timo Sirainen enum mail_cache_field_type type, unsigned int size)
9a48c2243fe98ca8393be7908f84d20c634bcdf9Timo Sirainen const struct mail_cache_field *field = &cache->fields[idx].field;
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen "registered field %s type changed", field->name);
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen if (field->field_size != size && field_has_fixed_size(type)) {
cd56a23e21f1df3f79648cf07e2f4385e2fadebbTimo Sirainen "registered field %s size changed", field->name);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenmail_cache_field_update(struct mail_cache *cache,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen i_assert(newfield->type < MAIL_CACHE_FIELD_COUNT);
571fd6ff94570ee11a72a20b649acfdac2495919Timo Sirainen /* are we still doing the initial cache field registering for
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen internal fields and for mail_*cache_fields settings? */
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen initial_registering = cache->file_fields_count == 0;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if ((newfield->decision & MAIL_CACHE_DECISION_FORCED) != 0 ||
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen ((orig->field.decision & MAIL_CACHE_DECISION_FORCED) == 0 &&
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainen if (orig->field.last_used < newfield->last_used) {
eb98a038ca8b0ef33d1d11794803ce09547496faTimo Sirainenvoid mail_cache_register_fields(struct mail_cache *cache,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen unsigned int new_idx;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen unsigned int i, j, registered_count;
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen for (i = 0; i < fields_count; i++) {
211ed7806d8715ec2280ffbf5d10f0d6e4f1beb2Timo Sirainen if (hash_table_lookup_full(cache->field_name_hash,
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen fields[i].idx = POINTER_CAST_TO(value, unsigned int);
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen /* check if the same header is being registered in the
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen same field array */
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen for (j = 0; j < i; j++) {
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen if (strcasecmp(fields[i].name, fields[j].name) == 0) {
556f95092c3bc850517d5ab2bb502024a55645f1Timo Sirainen /* @UNSAFE */
3d77cc0d502dc69ffe2afe318605964dd40b7b20Timo Sirainen i_realloc_type(cache->field_file_map, uint32_t,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen for (i = 0; i < fields_count; i++) {
3d77cc0d502dc69ffe2afe318605964dd40b7b20Timo Sirainen /* new index - save it */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen name = p_strdup(cache->field_pool, fields[i].name);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen cache->fields[idx].field.last_used = fields[i].last_used;
10ff47d5d6146995e16da00d36eca7d162064a7bTimo Sirainen if (!field_has_fixed_size(cache->fields[idx].field.type))
10ff47d5d6146995e16da00d36eca7d162064a7bTimo Sirainen cache->fields[idx].field.field_size = UINT_MAX;
10ff47d5d6146995e16da00d36eca7d162064a7bTimo Sirainen hash_table_insert(cache->field_name_hash, name,
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainenmail_cache_register_lookup(struct mail_cache *cache, const char *name)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (hash_table_lookup_full(cache->field_name_hash, name, &key, &value))
5238111c460098d9cc8cc22527026138a278b9a4Timo Sirainenmail_cache_register_get_field(struct mail_cache *cache, unsigned int field_idx)
5238111c460098d9cc8cc22527026138a278b9a4Timo Sirainenmail_cache_register_get_list(struct mail_cache *cache, pool_t pool,
68a4946b12583b88fa802e52ebee45cd96056772Timo Sirainen unsigned int *count_r)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen unsigned int i;
de954ff15b495be13007a8aca2c09fd1d356a283Timo Sirainen p_new(pool, struct mail_cache_field, cache->fields_count);
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainenmail_cache_header_fields_get_offset(struct mail_cache *cache,
d66ef20c30fee728899ee168c75fcc5ff8fbdac1Timo Sirainen const struct mail_cache_header_fields **field_hdr_r)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen const struct mail_cache_header_fields *field_hdr;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen struct mail_cache_header_fields tmp_field_hdr;
8f32e59200da904613506f5649ffa4d9f5989cebTimo Sirainen uint32_t offset = 0, next_offset, field_hdr_size;
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen unsigned int next_count = 0;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen /* find the latest header */
35283613d4c04ce18836e9fc431582c87b3710a0Timo Sirainen next_offset = cache->last_field_header_offset != 0 ?
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen mail_index_offset_to_uint32(cache->hdr->field_header_offset);
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen while (next_offset != 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen "next_offset in field header loops");
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen /* In Dovecot v2.2+ we don't try to use any holes,
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen so next_offset must always be larger than current offset.
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen also makes it easier to guarantee there aren't any loops
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen (which we don't bother doing for old files) */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (next_offset < offset && cache->hdr->minor_version != 0) {
0ce5f96804e81cb0f857e7df32c0272f1eed9377Timo Sirainen "next_offset in field header decreases");
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen if (cache->mmap_base != NULL || cache->map_with_read) {
6cc0546c058f3e6253c6f99727b28dd602712974Timo Sirainen ret = mail_cache_map(cache, offset, sizeof(*field_hdr),
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen "header field next_offset points outside file");
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* if we need to follow multiple offsets to get to
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen the last one, it's faster to just pread() the file
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen instead of going through cache */
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen mail_cache_set_syscall_error(cache, "pread()");
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen "header field next_offset points outside file");
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen mail_index_offset_to_uint32(field_hdr->next_offset);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen mail_cache_set_corrupted(cache, "missing header fields");
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (next_count > cache->index->optimization_set.cache.compress_header_continue_count)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen cache->need_compress_file_seq = cache->hdr->file_seq;
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen /* detect corrupted size later */
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen field_hdr_size = I_MAX(field_hdr->size, sizeof(*field_hdr));
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen /* invalidate the cache fields area to make sure we
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen get the latest cache decisions/last_used fields */
9de176ef7f3d28ff486c2a8805110b84389e4f19Timo Sirainen file_cache_invalidate(cache->file_cache, offset,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen ret = mail_cache_map(cache, offset, field_hdr_size, &data);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen "header field size outside file");
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainenint mail_cache_header_fields_read(struct mail_cache *cache)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen const struct mail_cache_header_fields *field_hdr;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (mail_cache_header_fields_get_offset(cache, &offset, &field_hdr) < 0)
98a73e104c7b9c3747053e63113451e24daf7f36Timo Sirainen /* no fields - the file is empty */
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen /* check the fixed size of the header. name[] has to be checked
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen separately */
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen if (field_hdr->fields_count > INT_MAX / MAIL_CACHE_FIELD_NAMES(1) ||
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen field_hdr->size < MAIL_CACHE_FIELD_NAMES(field_hdr->fields_count)) {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen mail_cache_set_corrupted(cache, "invalid field header size");
3d77cc0d502dc69ffe2afe318605964dd40b7b20Timo Sirainen i_realloc_type(cache->file_field_map, unsigned int,
63866b7a81355543832d3fe01cd744ddd4ea197bTimo Sirainen last_used = CONST_PTR_OFFSET(field_hdr, MAIL_CACHE_FIELD_LAST_USED());
63866b7a81355543832d3fe01cd744ddd4ea197bTimo Sirainen MAIL_CACHE_FIELD_SIZE(field_hdr->fields_count));
63866b7a81355543832d3fe01cd744ddd4ea197bTimo Sirainen MAIL_CACHE_FIELD_TYPE(field_hdr->fields_count));
989cafb9d84d8c98d6441fc1ab45b4c37762a98aTimo Sirainen MAIL_CACHE_FIELD_DECISION(field_hdr->fields_count));
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen MAIL_CACHE_FIELD_NAMES(field_hdr->fields_count));
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen end = CONST_PTR_OFFSET(field_hdr, field_hdr->size);
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen /* clear the old mapping */
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen max_drop_time = cache->index->map->hdr.day_stamp == 0 ? 0 :
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen cache->index->optimization_set.cache.unaccessed_field_drop_secs;
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen for (i = 0; i < field_hdr->fields_count; i++) {
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen for (p = names; p != end && *p != '\0'; p++) ;
8cdb3234fe3c77e477c7a0e6934678f58fc54d4dTimo Sirainen "field header names corrupted");
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen mail_cache_set_corrupted(cache, "field type corrupted");
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen "field decision type corrupted");
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen if (hash_table_lookup_full(cache->field_name_hash, names,
db3ebf659c17fc5ca348a997e6311d20cfbb78e9Timo Sirainen /* already exists, see if decision can be updated */
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen fidx = POINTER_CAST_TO(orig_value, unsigned int);
8ab3d88c56dff2f567193f80cc29821a64e576d1Timo Sirainen field.decision = decisions[i] & ~MAIL_CACHE_DECISION_FORCED;
71da447014454c84828d9dface77219875554d7dTimo Sirainen if (cache->field_file_map[fidx] != (uint32_t)-1) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* update last_used if it's newer than ours */
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen if ((time_t)last_used[i] > cache->fields[fidx].field.last_used)
e2ce8d4a6ac5d82a906178148453e7613fab9ba0Timo Sirainen cache->fields[fidx].field.last_used = last_used[i];
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (cache->fields[fidx].field.last_used < max_drop_time &&
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* time to drop this field. don't bother dropping
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen fields that have never been used. */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen cache->need_compress_file_seq = cache->hdr->file_seq;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenstatic void copy_to_buf(struct mail_cache *cache, buffer_t *dest, bool add_new,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen unsigned int i, field;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* copy the existing fields */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen for (i = 0; i < cache->file_fields_count; i++) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen data = CONST_PTR_OFFSET(&cache->fields[field], offset);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* copy newly wanted fields */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen data = CONST_PTR_OFFSET(&cache->fields[i], offset);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenstatic void copy_to_buf_byte(struct mail_cache *cache, buffer_t *dest,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen unsigned int i, field;
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen /* copy the existing fields */
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen for (i = 0; i < cache->file_fields_count; i++) {
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen data = CONST_PTR_OFFSET(&cache->fields[field], offset);
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen /* copy newly wanted fields */
d691782ae9eb4f6ab06bc42f1617e889e7c18a3bTimo Sirainen data = CONST_PTR_OFFSET(&cache->fields[i], offset);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenstatic int mail_cache_header_fields_update_locked(struct mail_cache *cache)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (mail_cache_header_fields_read(cache) < 0 ||
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen mail_cache_header_fields_get_offset(cache, &offset, NULL) < 0)
f28583935a4509d3f7932738bba6c548ef455c82Timo Sirainen ret = mail_cache_write(cache, buffer->data, buffer->used,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen MAIL_CACHE_FIELD_DECISION(cache->file_fields_count);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen ret = mail_cache_write(cache, buffer->data, buffer->used,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen for (i = 0; i < cache->file_fields_count; i++)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenint mail_cache_header_fields_update(struct mail_cache *cache)
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen ret = mail_cache_header_fields_update_locked(cache);
981139bb2e446bb2050c1158614725f8413fd709Timo Sirainen ret = mail_cache_header_fields_update_locked(cache);
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenvoid mail_cache_header_fields_get(struct mail_cache *cache, buffer_t *dest)
d78be924ad150840e018eda6a8a7d5e46a39bda2Timo Sirainen unsigned int field;
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* we have to keep the field order for the existing fields. */
8b247780e911909a9fdc47f69ce6d1478902ad98Timo Sirainen offsetof(struct mail_cache_field, field_size),
f28583935a4509d3f7932738bba6c548ef455c82Timo Sirainen /* add existing fields' names */
f28583935a4509d3f7932738bba6c548ef455c82Timo Sirainen for (i = 0; i < cache->file_fields_count; i++) {
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen /* add newly wanted fields' names */
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainenint mail_cache_header_fields_get_next_offset(struct mail_cache *cache,
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen if (mail_cache_header_fields_get_offset(cache, offset_r, NULL) < 0)
e3678f7bfba87b5aa1446a1a7b9928272b4f72a3Timo Sirainen *offset_r = offsetof(struct mail_cache_header,