mail-cache-fields.c revision 6157a322f2ac1ea1332d9003ecb0b11466aa8fe7
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen/* Copyright (c) 2004-2007 Dovecot authors, see the included COPYING file */
c25356d5978632df6203437e1953bcb29e0c736fTimo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen#include "lib.h"
cfdaa223525f87c9c980a25cc7bb6770a248d76aTimo Sirainen#include "ioloop.h"
cfdaa223525f87c9c980a25cc7bb6770a248d76aTimo Sirainen#include "buffer.h"
cfdaa223525f87c9c980a25cc7bb6770a248d76aTimo Sirainen#include "hash.h"
cfdaa223525f87c9c980a25cc7bb6770a248d76aTimo Sirainen#include "file-cache.h"
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen#include "read-full.h"
4f7c150ea0fb986d229379cda622cdcb2d827fd2Timo Sirainen#include "write-full.h"
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen#include "mmap-util.h"
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen#include "mail-cache-private.h"
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen#include <stddef.h>
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen#define CACHE_FIELD_IS_NEWLY_WANTED(cache, field_idx) \
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen ((cache)->field_file_map[field_idx] == (uint32_t)-1 && \
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen (cache)->fields[field_idx].used)
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenstatic bool field_has_fixed_size(enum mail_cache_field_type type)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen{
3c9783956dea385b322cd7fa6bf8c98c17a907a0Timo Sirainen switch (type) {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen case MAIL_CACHE_FIELD_FIXED_SIZE:
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen case MAIL_CACHE_FIELD_BITMASK:
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return TRUE;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen case MAIL_CACHE_FIELD_VARIABLE_SIZE:
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen case MAIL_CACHE_FIELD_STRING:
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen case MAIL_CACHE_FIELD_HEADER:
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen return FALSE;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen case MAIL_CACHE_FIELD_COUNT:
e65cc79f80577e83c706f0678c78e2c0bd91434fTimo Sirainen break;
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen }
7d6389e4053c2dac1fb37180b5756b00785983dcTimo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen i_unreached();
f1ddb98e6b639394ae205b305be1ddcfab102578Timo Sirainen return FALSE;
a399486f2d8d5bed51bc6344baba61a7f2b0dcdbTimo Sirainen}
40992309053d51192ae1b36d1dd6c057f2d37257Timo Sirainen
a399486f2d8d5bed51bc6344baba61a7f2b0dcdbTimo Sirainenstatic bool field_decision_is_valid(enum mail_cache_decision_type type)
a399486f2d8d5bed51bc6344baba61a7f2b0dcdbTimo Sirainen{
a399486f2d8d5bed51bc6344baba61a7f2b0dcdbTimo Sirainen switch (type & ~MAIL_CACHE_DECISION_FORCED) {
a399486f2d8d5bed51bc6344baba61a7f2b0dcdbTimo Sirainen case MAIL_CACHE_DECISION_NO:
a399486f2d8d5bed51bc6344baba61a7f2b0dcdbTimo Sirainen case MAIL_CACHE_DECISION_TEMP:
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen case MAIL_CACHE_DECISION_YES:
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen return TRUE;
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen default:
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen return FALSE;
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen }
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen}
b96dcd982888d89e6f2508258d6d9588d79c7a26Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenstatic int field_type_verify(struct mail_cache *cache, unsigned int idx,
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen enum mail_cache_field_type type, unsigned int size)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen{
b567e0172c73dcf7642462e86962060358dd5f28Timo Sirainen const struct mail_cache_field *field = &cache->fields[idx].field;
b567e0172c73dcf7642462e86962060358dd5f28Timo Sirainen
f8464772990b52cb8de4553bc1135adcf72813b8Timo Sirainen if (field->type != type) {
f4a19b0cf11cdff437571708d9d788d02a906a00Timo Sirainen mail_cache_set_corrupted(cache,
f4a19b0cf11cdff437571708d9d788d02a906a00Timo Sirainen "registered field %s type changed", field->name);
10c5fd417af4ee30b68c967f5e7d5a49f4f149b5Timo Sirainen return -1;
10c5fd417af4ee30b68c967f5e7d5a49f4f149b5Timo Sirainen }
10c5fd417af4ee30b68c967f5e7d5a49f4f149b5Timo Sirainen if (field->field_size != size && field_has_fixed_size(type)) {
10c5fd417af4ee30b68c967f5e7d5a49f4f149b5Timo Sirainen mail_cache_set_corrupted(cache,
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen "registered field %s size changed", field->name);
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen return -1;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
473080c7c0d25ddfdf77e7dfa0ba8f73c6c669d5Timo Sirainen return 0;
964c86de7158ccafdfe665853579d71232e2634eTimo Sirainen}
1f18053d463f0294387b5e4dd11f9010bda9a24eTimo Sirainen
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenvoid mail_cache_register_fields(struct mail_cache *cache,
e82af44fe25ca9b88210f313548dc08538e4a677Timo Sirainen struct mail_cache_field *fields,
e714eed72515794c46c6712a611e5ab924d903daTimo Sirainen unsigned int fields_count)
e714eed72515794c46c6712a611e5ab924d903daTimo Sirainen{
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen void *orig_key, *orig_value;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen char *name;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen unsigned int new_idx;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen unsigned int i, j;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen new_idx = cache->fields_count;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen for (i = 0; i < fields_count; i++) {
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen if (hash_lookup_full(cache->field_name_hash, fields[i].name,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen &orig_key, &orig_value)) {
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen i_assert(fields[i].type < MAIL_CACHE_FIELD_COUNT);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen fields[i].idx =
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen POINTER_CAST_TO(orig_value, unsigned int);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen (void)field_type_verify(cache, fields[i].idx,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen fields[i].type,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen fields[i].field_size);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen continue;
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen }
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen /* check if the same header is being registered in the
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen same field array */
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen for (j = 0; j < i; j++) {
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen if (strcasecmp(fields[i].name, fields[j].name) == 0) {
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen fields[i].idx = fields[j].idx;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen break;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen }
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen }
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen if (j == i)
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen fields[i].idx = new_idx++;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen }
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen if (new_idx == cache->fields_count)
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen return;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen /* @UNSAFE */
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen cache->fields = i_realloc(cache->fields,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen cache->fields_count * sizeof(*cache->fields),
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen new_idx * sizeof(*cache->fields));
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen cache->field_file_map =
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen i_realloc(cache->field_file_map,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen cache->fields_count * sizeof(*cache->field_file_map),
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen new_idx * sizeof(*cache->field_file_map));
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen for (i = 0; i < fields_count; i++) {
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen unsigned int idx = fields[i].idx;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen if (idx < cache->fields_count)
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen continue;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen
0a8926b91a84abf462afdc1ed95def229377d7ffTimo Sirainen /* new index - save it */
0a8926b91a84abf462afdc1ed95def229377d7ffTimo Sirainen name = p_strdup(cache->field_pool, fields[i].name);
0a8926b91a84abf462afdc1ed95def229377d7ffTimo Sirainen cache->fields[idx].field = fields[i];
0a8926b91a84abf462afdc1ed95def229377d7ffTimo Sirainen cache->fields[idx].field.name = name;
0a8926b91a84abf462afdc1ed95def229377d7ffTimo Sirainen cache->field_file_map[idx] = (uint32_t)-1;
0a8926b91a84abf462afdc1ed95def229377d7ffTimo Sirainen
0a8926b91a84abf462afdc1ed95def229377d7ffTimo Sirainen if (!field_has_fixed_size(cache->fields[idx].field.type))
0a8926b91a84abf462afdc1ed95def229377d7ffTimo Sirainen cache->fields[idx].field.field_size = (unsigned int)-1;
0a8926b91a84abf462afdc1ed95def229377d7ffTimo Sirainen
0a8926b91a84abf462afdc1ed95def229377d7ffTimo Sirainen hash_insert(cache->field_name_hash, name, POINTER_CAST(idx));
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen }
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen cache->fields_count = new_idx;
c4457e497e01b57565d24da624968699b166e02aTimo Sirainen}
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenunsigned int
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainenmail_cache_register_lookup(struct mail_cache *cache, const char *name)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen{
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen void *orig_key, *orig_value;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (hash_lookup_full(cache->field_name_hash, name,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen &orig_key, &orig_value))
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen return POINTER_CAST_TO(orig_value, unsigned int);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen else
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen return (unsigned int)-1;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen}
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainenconst struct mail_cache_field *
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainenmail_cache_register_get_list(struct mail_cache *cache, pool_t pool,
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen unsigned int *count_r)
63cde222abaaa2a9bdaa9a143698dbc8b23bd742Timo Sirainen{
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen struct mail_cache_field *list;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen unsigned int i;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen
e023e3c2677ab66d7a7445eae9caf3d739e199cbTimo Sirainen if (!cache->opened)
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen (void)mail_cache_open_and_verify(cache);
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen list = p_new(pool, struct mail_cache_field, cache->fields_count);
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen for (i = 0; i < cache->fields_count; i++) {
0a8926b91a84abf462afdc1ed95def229377d7ffTimo Sirainen list[i] = cache->fields[i].field;
964c86de7158ccafdfe665853579d71232e2634eTimo Sirainen list[i].name = p_strdup(pool, list[i].name);
a399486f2d8d5bed51bc6344baba61a7f2b0dcdbTimo Sirainen }
a399486f2d8d5bed51bc6344baba61a7f2b0dcdbTimo Sirainen
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen *count_r = cache->fields_count;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen return list;
fc4ff2356fee6389d4cf2b3f12f4098a436f0502Timo Sirainen}
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainenstatic int mail_cache_header_fields_get_offset(struct mail_cache *cache,
0a8926b91a84abf462afdc1ed95def229377d7ffTimo Sirainen uint32_t *offset_r, bool map)
16133a719ce8b6a5b8cedd721340cc1607c43433Timo Sirainen{
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen const struct mail_cache_header_fields *field_hdr;
964c86de7158ccafdfe665853579d71232e2634eTimo Sirainen struct mail_cache_header_fields tmp_field_hdr;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen uint32_t offset = 0, next_offset;
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen unsigned int next_count = 0;
964c86de7158ccafdfe665853579d71232e2634eTimo Sirainen int ret;
16133a719ce8b6a5b8cedd721340cc1607c43433Timo Sirainen
08aea01ef9a9d20703e0fcf8618e6195c0037a44Timo Sirainen if (MAIL_CACHE_IS_UNUSABLE(cache)) {
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen *offset_r = 0;
43d3ea2780b5f8557ede7b4c039e8f56cb8d357dTimo Sirainen return 0;
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen }
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen /* find the latest header */
3c9783956dea385b322cd7fa6bf8c98c17a907a0Timo Sirainen offset = 0;
3c9783956dea385b322cd7fa6bf8c98c17a907a0Timo Sirainen next_offset = cache->last_field_header_offset != 0 ?
ebfcfd258acc89633c47d9c3b0b40a1a3f75cdcbTimo Sirainen cache->last_field_header_offset :
d1f0acc7fc722e13e8296228703adfe8a884d59eTimo Sirainen mail_index_offset_to_uint32(cache->hdr->field_header_offset);
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen while (next_offset != 0) {
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen if (next_offset == offset) {
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen mail_cache_set_corrupted(cache,
0a8926b91a84abf462afdc1ed95def229377d7ffTimo Sirainen "next_offset in field header loops");
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen return -1;
3dadeec1ce7a5bf72fbd850658df1db3cedd4416Timo Sirainen }
3dadeec1ce7a5bf72fbd850658df1db3cedd4416Timo Sirainen offset = next_offset;
0a8926b91a84abf462afdc1ed95def229377d7ffTimo Sirainen
4261a8b43792dc4db4b39e6910319835b7450e84Timo Sirainen if (cache->mmap_base != NULL) {
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen if (mail_cache_map(cache, offset,
sizeof(*field_hdr)) < 0)
return -1;
field_hdr = CONST_PTR_OFFSET(cache->data, offset);
} else {
/* if we need to follow multiple offsets to get to
the last one, it's faster to just pread() the file
instead of going through cache */
ret = pread_full(cache->fd, &tmp_field_hdr,
sizeof(tmp_field_hdr), offset);
if (ret < 0) {
mail_cache_set_syscall_error(cache, "pread()");
return -1;
}
if (ret == 0) {
mail_cache_set_corrupted(cache,
"next_offset points outside file");
return -1;
}
field_hdr = &tmp_field_hdr;
}
next_offset =
mail_index_offset_to_uint32(field_hdr->next_offset);
next_count++;
}
if (offset == 0) {
mail_cache_set_corrupted(cache, "missing header fields");
return -1;
}
cache->last_field_header_offset = offset;
if (next_count > MAIL_CACHE_HEADER_FIELD_CONTINUE_COUNT)
cache->need_compress_file_seq = cache->hdr->file_seq;
if (map) {
if (cache->file_cache != NULL) {
/* we can't trust that the cached data is valid */
file_cache_invalidate(cache->file_cache, offset,
field_hdr->size);
}
if (mail_cache_map(cache, offset, field_hdr->size) < 0)
return -1;
}
*offset_r = offset;
return 0;
}
int mail_cache_header_fields_read(struct mail_cache *cache)
{
const struct mail_cache_header_fields *field_hdr = NULL;
struct mail_cache_field field;
const uint32_t *last_used, *sizes;
const uint8_t *types, *decisions;
const char *p, *names, *end;
void *orig_key, *orig_value;
unsigned int new_fields_count;
time_t max_drop_time;
uint32_t offset, i;
if (mail_cache_header_fields_get_offset(cache, &offset, TRUE) < 0)
return -1;
if (offset == 0) {
/* no fields - the file is empty */
return 0;
}
field_hdr = CONST_PTR_OFFSET(cache->data, offset);
if (offset + field_hdr->size > cache->mmap_length) {
mail_cache_set_corrupted(cache,
"field header points outside file");
return -1;
}
/* check the fixed size of the header. name[] has to be checked
separately */
if (field_hdr->size < sizeof(*field_hdr) +
field_hdr->fields_count * (sizeof(uint32_t)*2 + 1 + 2)) {
mail_cache_set_corrupted(cache, "invalid field header size");
return -1;
}
field_hdr = CONST_PTR_OFFSET(cache->data, offset);
new_fields_count = field_hdr->fields_count;
if (new_fields_count != 0) {
cache->file_field_map =
i_realloc(cache->file_field_map,
cache->file_fields_count *
sizeof(unsigned int),
new_fields_count * sizeof(unsigned int));
} else {
i_free_and_null(cache->file_field_map);
}
cache->file_fields_count = new_fields_count;
last_used = CONST_PTR_OFFSET(field_hdr, MAIL_CACHE_FIELD_LAST_USED());
sizes = CONST_PTR_OFFSET(field_hdr,
MAIL_CACHE_FIELD_SIZE(field_hdr->fields_count));
types = CONST_PTR_OFFSET(field_hdr,
MAIL_CACHE_FIELD_TYPE(field_hdr->fields_count));
decisions = CONST_PTR_OFFSET(field_hdr,
MAIL_CACHE_FIELD_DECISION(field_hdr->fields_count));
names = CONST_PTR_OFFSET(field_hdr,
MAIL_CACHE_FIELD_NAMES(field_hdr->fields_count));
end = CONST_PTR_OFFSET(field_hdr, field_hdr->size);
/* clear the old mapping */
for (i = 0; i < cache->fields_count; i++)
cache->field_file_map[i] = (uint32_t)-1;
max_drop_time = cache->index->map->hdr.day_stamp -
MAIL_CACHE_FIELD_DROP_SECS;
memset(&field, 0, sizeof(field));
for (i = 0; i < field_hdr->fields_count; i++) {
for (p = names; p != end && *p != '\0'; p++) ;
if (p == end || *names == '\0') {
mail_cache_set_corrupted(cache,
"field header names corrupted");
return -1;
}
if (types[i] > MAIL_CACHE_FIELD_COUNT) {
mail_cache_set_corrupted(cache, "field type corrupted");
return -1;
}
if (!field_decision_is_valid(decisions[i])) {
mail_cache_set_corrupted(cache,
"field decision type corrupted");
return -1;
}
if (hash_lookup_full(cache->field_name_hash, names,
&orig_key, &orig_value)) {
/* already exists, see if decision can be updated */
field.idx = POINTER_CAST_TO(orig_value, unsigned int);
if (!cache->fields[field.idx].decision_dirty) {
cache->fields[field.idx].field.decision =
decisions[i];
}
if (field_type_verify(cache, field.idx,
types[i], sizes[i]) < 0)
return -1;
} else {
field.name = names;
field.type = types[i];
field.field_size = sizes[i];
field.decision = decisions[i];
mail_cache_register_fields(cache, &field, 1);
}
if (cache->field_file_map[field.idx] != (uint32_t)-1) {
mail_cache_set_corrupted(cache,
"Duplicated field in header: %s", names);
return -1;
}
cache->fields[field.idx].used = TRUE;
cache->field_file_map[field.idx] = i;
cache->file_field_map[i] = field.idx;
/* update last_used if it's newer than ours */
if (last_used[i] > cache->fields[field.idx].last_used)
cache->fields[field.idx].last_used = last_used[i];
if (cache->fields[field.idx].last_used < max_drop_time &&
cache->fields[field.idx].last_used != 0) {
/* time to drop this field. don't bother dropping
fields that have never been used. */
cache->need_compress_file_seq = cache->hdr->file_seq;
}
names = p + 1;
}
return 0;
}
static void copy_to_buf(struct mail_cache *cache, buffer_t *dest, bool add_new,
size_t offset, size_t size)
{
const void *data;
unsigned int i, field;
/* copy the existing fields */
for (i = 0; i < cache->file_fields_count; i++) {
field = cache->file_field_map[i];
data = CONST_PTR_OFFSET(&cache->fields[field], offset);
buffer_append(dest, data, size);
}
if (!add_new)
return;
/* copy newly wanted fields */
for (i = 0; i < cache->fields_count; i++) {
if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i)) {
data = CONST_PTR_OFFSET(&cache->fields[i], offset);
buffer_append(dest, data, size);
}
}
}
static void copy_to_buf_byte(struct mail_cache *cache, buffer_t *dest,
bool add_new, size_t offset)
{
const int *data;
unsigned int i, field;
uint8_t byte;
/* copy the existing fields */
for (i = 0; i < cache->file_fields_count; i++) {
field = cache->file_field_map[i];
data = CONST_PTR_OFFSET(&cache->fields[field], offset);
byte = (uint8_t)*data;
buffer_append(dest, &byte, 1);
}
if (!add_new)
return;
/* copy newly wanted fields */
for (i = 0; i < cache->fields_count; i++) {
if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i)) {
data = CONST_PTR_OFFSET(&cache->fields[i], offset);
byte = (uint8_t)*data;
buffer_append(dest, &byte, 1);
}
}
}
static int mail_cache_header_fields_update_locked(struct mail_cache *cache)
{
buffer_t *buffer;
uint32_t i, offset, dec_offset;
int ret = 0;
if (mail_cache_header_fields_read(cache) < 0 ||
mail_cache_header_fields_get_offset(cache, &offset, FALSE) < 0)
return -1;
t_push();
buffer = buffer_create_dynamic(pool_datastack_create(), 256);
copy_to_buf(cache, buffer, FALSE,
offsetof(struct mail_cache_field_private, last_used),
sizeof(uint32_t));
ret = mail_cache_write(cache, buffer->data, buffer->used,
offset + MAIL_CACHE_FIELD_LAST_USED());
if (ret == 0) {
buffer_set_used_size(buffer, 0);
copy_to_buf_byte(cache, buffer, FALSE,
offsetof(struct mail_cache_field, decision));
dec_offset = offset +
MAIL_CACHE_FIELD_DECISION(cache->file_fields_count);
ret = mail_cache_write(cache, buffer->data, buffer->used,
dec_offset);
if (ret == 0) {
for (i = 0; i < cache->file_fields_count; i++)
cache->fields[i].decision_dirty = FALSE;
}
}
t_pop();
if (ret == 0)
cache->field_header_write_pending = FALSE;
return ret;
}
int mail_cache_header_fields_update(struct mail_cache *cache)
{
int ret;
if (cache->locked)
return mail_cache_header_fields_update_locked(cache);
if (mail_cache_lock(cache, NULL) <= 0)
return -1;
ret = mail_cache_header_fields_update_locked(cache);
if (mail_cache_unlock(cache) < 0)
ret = -1;
return ret;
}
void mail_cache_header_fields_get(struct mail_cache *cache, buffer_t *dest)
{
struct mail_cache_header_fields hdr;
unsigned int field;
const char *name;
uint32_t i;
memset(&hdr, 0, sizeof(hdr));
hdr.fields_count = cache->file_fields_count;
for (i = 0; i < cache->fields_count; i++) {
if (cache->fields[i].last_used == 0) {
/* return newly added fields' last_used as
the current time */
cache->fields[i].last_used = ioloop_time;
}
if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i))
hdr.fields_count++;
}
buffer_append(dest, &hdr, sizeof(hdr));
/* we have to keep the field order for the existing fields. */
copy_to_buf(cache, dest, TRUE,
offsetof(struct mail_cache_field_private, last_used),
sizeof(uint32_t));
copy_to_buf(cache, dest, TRUE,
offsetof(struct mail_cache_field, field_size),
sizeof(uint32_t));
copy_to_buf_byte(cache, dest, TRUE,
offsetof(struct mail_cache_field, type));
copy_to_buf_byte(cache, dest, TRUE,
offsetof(struct mail_cache_field, decision));
i_assert(dest->used == sizeof(hdr) +
(sizeof(uint32_t)*2 + 2) * hdr.fields_count);
/* add existing fields' names */
for (i = 0; i < cache->file_fields_count; i++) {
field = cache->file_field_map[i];
name = cache->fields[field].field.name;
buffer_append(dest, name, strlen(name)+1);
}
/* add newly wanted fields' names */
for (i = 0; i < cache->fields_count; i++) {
if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i)) {
name = cache->fields[i].field.name;
buffer_append(dest, name, strlen(name)+1);
}
}
hdr.size = dest->used;
buffer_write(dest, 0, &hdr, sizeof(hdr));
if ((hdr.size & 3) != 0)
buffer_append_zero(dest, 4 - (hdr.size & 3));
}
int mail_cache_header_fields_get_next_offset(struct mail_cache *cache,
uint32_t *offset_r)
{
if (mail_cache_header_fields_get_offset(cache, offset_r, FALSE) < 0)
return -1;
if (*offset_r == 0) {
*offset_r = offsetof(struct mail_cache_header,
field_header_offset);
} else {
*offset_r += offsetof(struct mail_cache_header_fields,
next_offset);
}
return 0;
}