mail-cache-fields.c revision a16d9a651aaa36a308f1aaae87e73e143fdff887
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2004-2013 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define CACHE_FIELD_IS_NEWLY_WANTED(cache, field_idx) \
66ae183b6e895216037bd921367670f4b0665911Timo Sirainen ((cache)->field_file_map[field_idx] == (uint32_t)-1 && \
b66a207ddcfc72a634186ec7e9a82df28ffc1d4eTimo Sirainenstatic bool field_has_fixed_size(enum mail_cache_field_type type)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic bool field_decision_is_valid(enum mail_cache_decision_type type)
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainenstatic int field_type_verify(struct mail_cache *cache, unsigned int idx,
8ae72ad7d0c69e972cfa65d1e2ce4e3e9a8b765cTimo Sirainen enum mail_cache_field_type type, unsigned int size)
b66a207ddcfc72a634186ec7e9a82df28ffc1d4eTimo Sirainen const struct mail_cache_field *field = &cache->fields[idx].field;
b66a207ddcfc72a634186ec7e9a82df28ffc1d4eTimo Sirainen "registered field %s type changed", field->name);
b66a207ddcfc72a634186ec7e9a82df28ffc1d4eTimo Sirainen if (field->field_size != size && field_has_fixed_size(type)) {
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen "registered field %s size changed", field->name);
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenmail_cache_field_update(struct mail_cache *cache,
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen i_assert(newfield->type < MAIL_CACHE_FIELD_COUNT);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if ((newfield->decision & MAIL_CACHE_DECISION_FORCED) != 0 ||
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (orig->field.last_used < newfield->last_used) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_cache_register_fields(struct mail_cache *cache,
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen unsigned int new_idx;
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen unsigned int i, j;
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen for (i = 0; i < fields_count; i++) {
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen if (hash_table_lookup_full(cache->field_name_hash,
80fc743146da5130de34174cdaad2576f103723fTimo Sirainen fields[i].idx = POINTER_CAST_TO(value, unsigned int);
e704b4ab2c204e538f87b2a90aca68c0deac6296Timo Sirainen /* check if the same header is being registered in the
e704b4ab2c204e538f87b2a90aca68c0deac6296Timo Sirainen same field array */
e704b4ab2c204e538f87b2a90aca68c0deac6296Timo Sirainen for (j = 0; j < i; j++) {
96595bf98ed9264b2b35700a640daf53debc3082Timo Sirainen if (strcasecmp(fields[i].name, fields[j].name) == 0) {
cded712c19ea8fb43c22726331de26b8a8dd234bTimo Sirainen /* @UNSAFE */
cded712c19ea8fb43c22726331de26b8a8dd234bTimo Sirainen cache->fields_count * sizeof(*cache->field_file_map),
cded712c19ea8fb43c22726331de26b8a8dd234bTimo Sirainen for (i = 0; i < fields_count; i++) {
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen /* new index - save it */
20a802016205bbcafc90f164f769ea801f88d014Timo Sirainen name = p_strdup(cache->field_pool, fields[i].name);
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen cache->fields[idx].field.last_used = fields[i].last_used;
20a802016205bbcafc90f164f769ea801f88d014Timo Sirainen if (!field_has_fixed_size(cache->fields[idx].field.type))
20a802016205bbcafc90f164f769ea801f88d014Timo Sirainen cache->fields[idx].field.field_size = UINT_MAX;
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen hash_table_insert(cache->field_name_hash, name,
b66a207ddcfc72a634186ec7e9a82df28ffc1d4eTimo Sirainenmail_cache_register_lookup(struct mail_cache *cache, const char *name)
b66a207ddcfc72a634186ec7e9a82df28ffc1d4eTimo Sirainen if (hash_table_lookup_full(cache->field_name_hash, name, &key, &value))
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainenmail_cache_register_get_field(struct mail_cache *cache, unsigned int field_idx)
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainenmail_cache_register_get_list(struct mail_cache *cache, pool_t pool,
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainen unsigned int *count_r)
47e0598840ecffa364ebed523e06939e22738f06Timo Sirainen unsigned int i;
bbf796c17f02538058d7559bfe96d677e5b55015Timo Sirainen p_new(pool, struct mail_cache_field, cache->fields_count);
1e923fcf497665fe071a154c31fb452766b0b2deTimo Sirainenmail_cache_header_fields_get_offset(struct mail_cache *cache,
d161e3c2cde2bd8d5917840f68823a2259ed426eTimo Sirainen const struct mail_cache_header_fields **field_hdr_r)
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen const struct mail_cache_header_fields *field_hdr;
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen struct mail_cache_header_fields tmp_field_hdr;
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen uint32_t offset = 0, next_offset, field_hdr_size;
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen unsigned int next_count = 0;
ea91861234475257f436dc07925f80cf4ac32b71Timo Sirainen /* find the latest header */
ea91861234475257f436dc07925f80cf4ac32b71Timo Sirainen next_offset = cache->last_field_header_offset != 0 ?
ea91861234475257f436dc07925f80cf4ac32b71Timo Sirainen mail_index_offset_to_uint32(cache->hdr->field_header_offset);
ea91861234475257f436dc07925f80cf4ac32b71Timo Sirainen while (next_offset != 0) {
ea91861234475257f436dc07925f80cf4ac32b71Timo Sirainen "next_offset in field header loops");
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen if (cache->mmap_base != NULL || cache->map_with_read) {
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen ret = mail_cache_map(cache, offset, sizeof(*field_hdr),
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen "header field next_offset points outside file");
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen /* if we need to follow multiple offsets to get to
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen the last one, it's faster to just pread() the file
c5454841b5067a22827556ca9bc7935d190f57baTimo Sirainen instead of going through cache */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen mail_cache_set_syscall_error(cache, "pread()");
de12ff295bb3d0873b4dced5840612cbacd635efTimo Sirainen "header field next_offset points outside file");
da985034a708db2f61394b30d117050ae6829ee5Timo Sirainen mail_index_offset_to_uint32(field_hdr->next_offset);
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen mail_cache_set_corrupted(cache, "missing header fields");
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen if (next_count > MAIL_CACHE_HEADER_FIELD_CONTINUE_COUNT)
de12ff295bb3d0873b4dced5840612cbacd635efTimo Sirainen cache->need_compress_file_seq = cache->hdr->file_seq;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen /* detect corrupted size later */
5626ae5e3316eced244adb6485c0927f1c7fdc41Timo Sirainen field_hdr_size = I_MAX(field_hdr->size, sizeof(*field_hdr));
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen /* invalidate the cache fields area to make sure we
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen get the latest cache decisions/last_used fields */
5626ae5e3316eced244adb6485c0927f1c7fdc41Timo Sirainen file_cache_invalidate(cache->file_cache, offset,
c27f03fa8fd2ef4acd1db814fae7d90e0eb9d3aeTimo Sirainen ret = mail_cache_map(cache, offset, field_hdr_size, &data);
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen "header field size outside file");
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainenint mail_cache_header_fields_read(struct mail_cache *cache)
f23298fea47eecbeded985ee2537a34c4c4ef56bTimo Sirainen const struct mail_cache_header_fields *field_hdr;
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen if (mail_cache_header_fields_get_offset(cache, &offset, &field_hdr) < 0)
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen /* no fields - the file is empty */
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen /* check the fixed size of the header. name[] has to be checked
6825360d446542046757b06064282301c4c6b27cTimo Sirainen separately */
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen field_hdr->fields_count * (sizeof(uint32_t)*2 + 1 + 2)) {
a75d470c9223a75801418fcdda258885c36317e0Timo Sirainen mail_cache_set_corrupted(cache, "invalid field header size");
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen sizeof(unsigned int),
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen new_fields_count * sizeof(unsigned int));
6825360d446542046757b06064282301c4c6b27cTimo Sirainen last_used = CONST_PTR_OFFSET(field_hdr, MAIL_CACHE_FIELD_LAST_USED());
6825360d446542046757b06064282301c4c6b27cTimo Sirainen MAIL_CACHE_FIELD_SIZE(field_hdr->fields_count));
6825360d446542046757b06064282301c4c6b27cTimo Sirainen MAIL_CACHE_FIELD_TYPE(field_hdr->fields_count));
c8d093d149253fe8faec267c5057f45fe626f84cTimo Sirainen MAIL_CACHE_FIELD_DECISION(field_hdr->fields_count));
6825360d446542046757b06064282301c4c6b27cTimo Sirainen MAIL_CACHE_FIELD_NAMES(field_hdr->fields_count));
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen end = CONST_PTR_OFFSET(field_hdr, field_hdr->size);
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen /* clear the old mapping */
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen max_drop_time = cache->index->map->hdr.day_stamp == 0 ? 0 :
f5c53272acbe1517440aa9c277133babc887ee84Timo Sirainen cache->index->map->hdr.day_stamp - MAIL_CACHE_FIELD_DROP_SECS;
f5c53272acbe1517440aa9c277133babc887ee84Timo Sirainen for (i = 0; i < field_hdr->fields_count; i++) {
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen for (p = names; p != end && *p != '\0'; p++) ;
16c89b1260c9d07c01c83a9219424d3727069b2eTimo Sirainen "field header names corrupted");
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen mail_cache_set_corrupted(cache, "field type corrupted");
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen "field decision type corrupted");
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen if (hash_table_lookup_full(cache->field_name_hash, names,
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen /* already exists, see if decision can be updated */
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen fidx = POINTER_CAST_TO(orig_value, unsigned int);
4a09c57f1c66b4a8880bcc12b567bb42c3549f52Timo Sirainen if (cache->field_file_map[fidx] != (uint32_t)-1) {
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen /* update last_used if it's newer than ours */
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen if ((time_t)last_used[i] > cache->fields[fidx].field.last_used)
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen cache->fields[fidx].field.last_used = last_used[i];
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen if (cache->fields[fidx].field.last_used < max_drop_time &&
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen /* time to drop this field. don't bother dropping
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen fields that have never been used. */
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen cache->need_compress_file_seq = cache->hdr->file_seq;
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainenstatic void copy_to_buf(struct mail_cache *cache, buffer_t *dest, bool add_new,
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen unsigned int i, field;
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen /* copy the existing fields */
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen for (i = 0; i < cache->file_fields_count; i++) {
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen data = CONST_PTR_OFFSET(&cache->fields[field], offset);
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen /* copy newly wanted fields */
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen data = CONST_PTR_OFFSET(&cache->fields[i], offset);
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainenstatic void copy_to_buf_byte(struct mail_cache *cache, buffer_t *dest,
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen unsigned int i, field;
3f2ad7b8c3a243dabcba469c8a331423d036f3fcTimo Sirainen /* copy the existing fields */
2a34e2be33f8a17d21384a5527ed9f75f4d270e0Timo Sirainen for (i = 0; i < cache->file_fields_count; i++) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen data = CONST_PTR_OFFSET(&cache->fields[field], offset);
c53e8ee216904ffe6de4f6518d9f9f5107b7610eTimo Sirainen /* copy newly wanted fields */
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen data = CONST_PTR_OFFSET(&cache->fields[i], offset);
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainenstatic int mail_cache_header_fields_update_locked(struct mail_cache *cache)
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen if (mail_cache_header_fields_read(cache) < 0 ||
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen mail_cache_header_fields_get_offset(cache, &offset, FALSE) < 0)
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen buffer = buffer_create_dynamic(pool_datastack_create(), 256);
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen ret = mail_cache_write(cache, buffer->data, buffer->used,
e4b09b008ab544eb8994beecbfffefa21d855e43Timo Sirainen MAIL_CACHE_FIELD_DECISION(cache->file_fields_count);
e3689d0f073341e844638f34e1e4d0b7bb053cc8Timo Sirainen ret = mail_cache_write(cache, buffer->data, buffer->used,
e3689d0f073341e844638f34e1e4d0b7bb053cc8Timo Sirainen for (i = 0; i < cache->file_fields_count; i++)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint mail_cache_header_fields_update(struct mail_cache *cache)
d22390f33eedbd2413debabc0662dde5241b1aa6Timo Sirainen ret = mail_cache_header_fields_update_locked(cache);
14b8bbb81e0b546436d4d5d5f38e45027c146b9bTimo Sirainen ret = mail_cache_header_fields_update_locked(cache);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_cache_header_fields_get(struct mail_cache *cache, buffer_t *dest)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int field;
2d49f150b4bce6f2f59a84e268e4777901c3e42cTimo Sirainen /* we have to keep the field order for the existing fields. */
af1e2b2ab5d1c5ca5afe482ef8c8161c17acc190Timo Sirainen offsetof(struct mail_cache_field, field_size),
2bda0d095e029ec72ec683b4186e04f322b4978eTimo Sirainen /* add existing fields' names */
2bda0d095e029ec72ec683b4186e04f322b4978eTimo Sirainen for (i = 0; i < cache->file_fields_count; i++) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* add newly wanted fields' names */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainenint mail_cache_header_fields_get_next_offset(struct mail_cache *cache,
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (mail_cache_header_fields_get_offset(cache, offset_r, FALSE) < 0)
e4b242684975b4d3702ca79bfd56452fe139a2bfTimo Sirainen *offset_r = offsetof(struct mail_cache_header,