mail-cache-fields.c revision e015e2f7e7f48874495f9df8b0dd192b7ffcb5cc
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (C) 2004 Timo Sirainen */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen#include "buffer.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include "hash.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include "mail-cache-private.h"
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen#include <stddef.h>
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen#define CACHE_HDR_PREFETCH 1024
bb10ebcf076c959c752f583746d83805d7686df8Timo Sirainen
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainenstatic const unsigned char *null4[] = { 0, 0, 0, 0 };
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainenvoid mail_cache_register_fields(struct mail_cache *cache,
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen struct mail_cache_field *fields,
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen size_t fields_count)
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen{
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen void *orig_key, *orig_value;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen unsigned int new_idx;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen size_t i;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen new_idx = cache->fields_count;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen for (i = 0; i < fields_count; i++) {
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen if (hash_lookup_full(cache->field_name_hash, fields[i].name,
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen &orig_key, &orig_value)) {
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen fields[i].idx =
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen POINTER_CAST_TO(orig_value, unsigned int);
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen continue;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen fields[i].idx = new_idx++;
db87d16551d1081ada01f787ea21aa3ed1402c31Timo Sirainen }
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen if (new_idx == cache->fields_count)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen /* @UNSAFE */
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen cache->fields = p_realloc(cache->field_pool, cache->fields,
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen cache->fields_count * sizeof(*cache->fields),
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen new_idx * sizeof(*cache->fields));
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen cache->field_file_map =
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen p_realloc(cache->field_pool, cache->field_file_map,
c251a38df327599a62d341bf5c2282f31352faa5Timo Sirainen cache->fields_count * sizeof(*cache->field_file_map),
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen new_idx * sizeof(*cache->field_file_map));
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen for (i = 0; i < fields_count; i++) {
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen unsigned int idx = fields[i].idx;
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if (idx < cache->fields_count)
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen continue;
beb6125ee872e7fed57745ab33e6de99639180f3Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen /* new index - save it */
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen cache->fields[idx] = fields[i];
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen cache->fields[idx].name =
893e5bbd5184ec5c21f47c67c8ea6efbea41f7d0Timo Sirainen p_strdup(cache->field_pool, fields[i].name);
faed8babca9914257f34fb2e603d74016d563b2dTimo Sirainen cache->field_file_map[idx] = (uint32_t)-1;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen switch (cache->fields[idx].type) {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen case MAIL_CACHE_FIELD_FIXED_SIZE:
1175f27441385a7011629f295f42708f9a3a4ffcTimo Sirainen case MAIL_CACHE_FIELD_BITMASK:
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen break;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen case MAIL_CACHE_FIELD_VARIABLE_SIZE:
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen case MAIL_CACHE_FIELD_STRING:
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen case MAIL_CACHE_FIELD_HEADER:
46c31f64b9f0949f00b7819f45b22f2d64b2ea27Timo Sirainen cache->fields[idx].field_size = (unsigned int)-1;
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen break;
687bb904e1bb76c21a6e392f60c990486b298ea4Timo Sirainen }
687bb904e1bb76c21a6e392f60c990486b298ea4Timo Sirainen
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen hash_insert(cache->field_name_hash,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen (char *)cache->fields[idx].name,
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen POINTER_CAST(idx));
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen }
aa38d1a0945f0bc13a225d043f53fad2eec666b1Timo Sirainen cache->fields_count = new_idx;
ae8817f05005f57bba32479a610b52d083e2b6ebTimo Sirainen}
a022088674a5ae72ed29ae001834cbad62a4f19fTimo Sirainen
a022088674a5ae72ed29ae001834cbad62a4f19fTimo Sirainenunsigned int
a022088674a5ae72ed29ae001834cbad62a4f19fTimo Sirainenmail_cache_register_lookup(struct mail_cache *cache, const char *name)
73e7998716853b5b7621c06aea0022dccda70ad1Timo Sirainen{
a2cbf1d392ee983520451bc9b849a490f28ac298Timo Sirainen void *orig_key, *orig_value;
a2cbf1d392ee983520451bc9b849a490f28ac298Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (hash_lookup_full(cache->field_name_hash, name,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen &orig_key, &orig_value))
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen return POINTER_CAST_TO(orig_value, unsigned int);
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen else
d6badc27cd6e8d3398877b6766cb0aaeef3a7800Timo Sirainen return (unsigned int)-1;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen}
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenstatic int mail_cache_header_fields_get_offset(struct mail_cache *cache,
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen uint32_t *offset_r)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen{
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainen const struct mail_cache_header_fields *field_hdr;
985fa802913c96ce6f2e25bbc788ee39c416a7e0Timo Sirainen uint32_t offset, next_offset;
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen
985fa802913c96ce6f2e25bbc788ee39c416a7e0Timo Sirainen if (MAIL_CACHE_IS_UNUSABLE(cache)) {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen *offset_r = 0;
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen return 0;
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
/* find the latest header */
offset = 0;
next_offset =
mail_cache_offset_to_uint32(cache->hdr->field_header_offset);
while (next_offset != 0) {
offset = next_offset;
if (mail_cache_map(cache, offset,
sizeof(*field_hdr) + CACHE_HDR_PREFETCH) < 0)
return -1;
field_hdr = CONST_PTR_OFFSET(cache->mmap_base, offset);
next_offset =
mail_cache_offset_to_uint32(field_hdr->next_offset);
}
*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;
uint32_t offset, i;
if (mail_cache_header_fields_get_offset(cache, &offset) < 0)
return -1;
if (offset == 0) {
/* no fields - the file is empty */
return 0;
}
field_hdr = CONST_PTR_OFFSET(cache->mmap_base, 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;
}
if (field_hdr->size > sizeof(*field_hdr) + CACHE_HDR_PREFETCH) {
if (mail_cache_map(cache, offset, field_hdr->size) < 0)
return -1;
}
field_hdr = CONST_PTR_OFFSET(cache->mmap_base, offset);
cache->file_field_map =
i_realloc(cache->file_field_map,
cache->file_fields_count * sizeof(unsigned int),
field_hdr->fields_count * sizeof(unsigned int));
cache->file_fields_count = field_hdr->fields_count;
last_used = MAIL_CACHE_FIELD_LAST_USED(field_hdr);
sizes = MAIL_CACHE_FIELD_SIZE(field_hdr);
types = MAIL_CACHE_FIELD_TYPE(field_hdr);
decisions = MAIL_CACHE_FIELD_DECISION(field_hdr);
names = MAIL_CACHE_FIELD_NAMES(field_hdr);
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;
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) {
mail_cache_set_corrupted(cache,
"field header names corrupted");
return -1;
}
field.name = names;
field.type = types[i];
field.field_size = sizes[i];
field.decision = decisions[i];
field.last_used = (time_t)last_used[i];
mail_cache_register_fields(cache, &field, 1);
cache->field_file_map[field.idx] = i;
cache->file_field_map[i] = field.idx;
names = p + 1;
}
return 0;
}
int mail_cache_header_fields_update(struct mail_cache *cache)
{
int locked = cache->locked;
if (!locked) {
if (mail_cache_lock(cache) <= 0)
return -1;
}
// FIXME
if (!locked)
mail_cache_unlock(cache);
}
#define UGLY_COPY_MACRO(field_name, type) \
for (i = 0; i < cache->file_fields_count; i++) { \
field = cache->file_field_map[i]; \
field_name = (type)cache->fields[field].field_name; \
buffer_append(dest, &field_name, sizeof(field_name)); \
} \
for (i = 0; i < cache->fields_count; i++) { \
if (cache->field_file_map[i] != (uint32_t)-1) \
continue; \
field_name = (type)cache->fields[i].field_name; \
buffer_append(dest, &field_name, sizeof(field_name)); \
}
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, last_used, field_size;
uint8_t type, decision;
memset(&hdr, 0, sizeof(hdr));
hdr.fields_count = cache->fields_count;
buffer_append(dest, &hdr, sizeof(hdr));
/* we have to keep the field order for the existing fields. */
UGLY_COPY_MACRO(last_used, uint32_t);
UGLY_COPY_MACRO(field_size, uint32_t);
UGLY_COPY_MACRO(type, uint8_t);
UGLY_COPY_MACRO(decision, uint8_t);
for (i = 0; i < cache->file_fields_count; i++) {
field = cache->file_field_map[i];
name = cache->fields[field].name;
buffer_append(dest, name, strlen(name)+1);
}
for (i = 0; i < cache->fields_count; i++) {
if (cache->field_file_map[i] != (uint32_t)-1)
continue;
name = cache->fields[i].name;
buffer_append(dest, name, strlen(name)+1);
}
hdr.size = buffer_get_used_size(dest);
buffer_write(dest, 0, &hdr, sizeof(hdr));
if ((hdr.size & 3) != 0)
buffer_append(dest, null4, 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) < 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;
}