mail-cache.c revision 5024402a628e9a6fc21eb8cdf2778486c6f2f92c
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (C) 2003 Timo Sirainen */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#define _XOPEN_SOURCE 500 /* for pwrite() / Linux */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen/* Never compress the file if it's smaller than this */
cf2e6953d03a1c22f272ec19432fc03c136ac1bbTimo Sirainen/* Compress the file when deleted space reaches n% of total size */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen/* Compress the file when n% of rows contain continued rows.
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen 200% means that there's 2 continued rows per record. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen/* Initial size for the file */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen#define MAIL_CACHE_INITIAL_SIZE (sizeof(struct mail_cache_header) + 10240)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen/* When more space is needed, grow the file n% larger than the previous size */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen ((char *) (cache)->mmap_base + ((data_pos) & ~3)))
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen uint32_t used_fields; /* enum mail_cache_field */
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen uint32_t header_offsets[MAIL_CACHE_HEADERS_COUNT];
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainen uint32_t size; /* full record size, including this header */
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen uint32_t split_offsets[MAIL_CACHE_HEADERS_COUNT];
78fa3c578c14ee8a612f86cf73b6181c7f16463fTimo Sirainen const char *const *split_headers[MAIL_CACHE_HEADERS_COUNT];
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainen unsigned int locks;
efc5c69c572e83db7bf7eab5d4698c0ab0d3d886Timo Sirainen sizeof(struct mail_sent_date),
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* variable sized */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenenum mail_cache_field mail_cache_header_fields[MAIL_CACHE_HEADERS_COUNT] = {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic const unsigned char *null4[] = { 0, 0, 0, 0 };
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic const char *
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenmail_cache_get_header_fields_str(struct mail_cache *cache, unsigned int idx);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic int mail_cache_write(struct mail_cache_transaction_ctx *ctx);
7487ff578435377bbeefffdbfb78ca09ed1292dfTimo Sirainenstatic int mail_cache_set_syscall_error(struct mail_cache *cache,
614529ee060755c0b282102b70daf56bcd64222dTimo Sirainen index_set_error(cache->index, "%s failed with index cache file %s: %m",
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic int mail_cache_create_memory(struct mail_cache *cache,
e1f866daa1bd1a5cd7516f3b19c6f197bcf6cc8aTimo Sirainen cache->mmap_base = mmap_anon(cache->mmap_length);
e1f866daa1bd1a5cd7516f3b19c6f197bcf6cc8aTimo Sirainen index_set_error(cache->index, "mmap_anon(%"PRIuSIZE_T")",
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen cache->filepath = i_strdup_printf("(in-memory index cache for %s)",
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic void mail_cache_file_close(struct mail_cache *cache)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (munmap_anon(cache->mmap_base, cache->mmap_length) < 0)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen mail_cache_set_syscall_error(cache, "munmap_anon()");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (munmap(cache->mmap_base, cache->mmap_length) < 0)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen mail_cache_set_syscall_error(cache, "munmap()");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen mail_cache_set_syscall_error(cache, "close()");
f5e1d3d6b34ec152aa1ff15c7bd3d3552e9227eaTimo Sirainenstatic int mail_cache_file_reopen(struct mail_cache *cache)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* cache was set corrupted, we'll have to quit */
3a0f9aa9504497e4e47f32df54fbf47fdc7423b6Timo Sirainen return mail_cache_set_syscall_error(cache, "open()");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic int mmap_verify_header(struct mail_cache *cache)
43d7e7ce608f5451e4907b5f5c48c00beb265802Timo Sirainen /* check that the header is still ok */
43d7e7ce608f5451e4907b5f5c48c00beb265802Timo Sirainen if (cache->mmap_length < sizeof(struct mail_cache_header))
43d7e7ce608f5451e4907b5f5c48c00beb265802Timo Sirainen return mail_cache_set_corrupted(cache, "File too small");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (cache->header->indexid != cache->index->indexid) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* index id changed */
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen mail_cache_set_corrupted(cache, "indexid changed");
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen cache->index->inconsistent = TRUE; /* easiest way to rebuild */
55bc6a7a0940ec48a68558ef70838991c5d301d2Timo Sirainen /* we've updated used_file_size, do nothing */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen cache->used_file_size = nbo_to_uint32(hdr->used_file_size);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* only check the header if we're locked */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (cache->used_file_size < sizeof(struct mail_cache_header)) {
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen mail_cache_set_corrupted(cache, "used_file_size too small");
601b455f4d5e780044b9e4fac5f687c1b07ae145Timo Sirainen if ((cache->used_file_size % sizeof(uint32_t)) != 0) {
cdc8485491045d82bb98405d4b995f277d12838eTimo Sirainen mail_cache_set_corrupted(cache, "used_file_size not aligned");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (cache->used_file_size > cache->mmap_length) {
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen /* maybe a crash truncated the file - just fix it */
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen hdr->used_file_size = uint32_to_nbo(cache->mmap_length & ~3);
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen if (msync(cache->mmap_base, sizeof(*hdr), MS_SYNC) < 0)
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen return mail_cache_set_syscall_error(cache, "msync()");
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainenstatic int mmap_update_nocheck(struct mail_cache *cache,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen cache->header->indexid != cache->index->indexid) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* indexid changed, most likely it was rebuilt.
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen try reopening. */
cf0ad1a0bddb0787f3d7b408a96d721a8b2a98a3Timo Sirainen /* force mmap refresh */
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen if (size != 0 && offset < cache->mmap_length &&
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* already mapped */
4f4943f6ef1bc45c23de73eebe83779712b3c8cbTimo Sirainen /* in the middle of transaction - write the changes */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (msync(cache->mmap_base, cache->mmap_length,
bad5fa318c6c1384ab83bd72d53ce06593274c18Timo Sirainen mail_cache_set_syscall_error(cache, "msync()");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (munmap(cache->mmap_base, cache->mmap_length) < 0)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen mail_cache_set_syscall_error(cache, "munmap()");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* map the whole file */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen cache->mmap_base = mmap_rw_file(cache->fd, &cache->mmap_length);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return mail_cache_set_syscall_error(cache, "mmap()");
6c2ce1d5bf17b21e804a079eb0f973b7ab83e0d8Timo Sirainenstatic int mmap_update(struct mail_cache *cache, size_t offset, size_t size)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen return mmap_update_nocheck(cache, offset, size) &&
6fdfa4d4cf14d1d7764d7faa8258f112e39c8dbeTimo Sirainenstatic int mail_cache_open_and_verify(struct mail_cache *cache, int silent)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen mail_cache_set_syscall_error(cache, "open()");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen mail_cache_set_syscall_error(cache, "fstat()");
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (st.st_size < sizeof(struct mail_cache_header))
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (!mmap_update_nocheck(cache, 0, sizeof(struct mail_cache_header)))
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* verify that this really is the cache for wanted index */
614529ee060755c0b282102b70daf56bcd64222dTimo Sirainenstatic void mail_index_clear_cache_offsets(struct mail_index *index)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic int mail_cache_open_or_create_file(struct mail_cache *cache,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen cache->filepath = i_strconcat(cache->index->filepath,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen ret = mail_cache_open_and_verify(cache, FALSE);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* we'll have to clear cache_offsets which requires exclusive lock */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (!mail_index_set_lock(cache->index, MAIL_LOCK_EXCLUSIVE))
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* maybe a rebuild.. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen fd = file_dotlock_open(cache->filepath, NULL, MAIL_CACHE_LOCK_TIMEOUT,
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen mail_cache_set_syscall_error(cache, "file_dotlock_open()");
ca502ca792708238ac2c23481c3bf94eda1f743fTimo Sirainen /* see if someone else just created the cache file */
ca502ca792708238ac2c23481c3bf94eda1f743fTimo Sirainen ret = mail_cache_open_and_verify(cache, TRUE);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen (void)file_dotlock_delete(cache->filepath, fd);
ca502ca792708238ac2c23481c3bf94eda1f743fTimo Sirainen /* rebuild then */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen mail_cache_set_syscall_error(cache, "write_full()");
63849db64682675a2fd3e1aea05c10ecbc6d473aTimo Sirainen (void)file_dotlock_delete(cache->filepath, fd);
dfcc56dc6c485fe45a3fa18325f047bfaae65019Timo Sirainen if (file_set_size(fd, MAIL_CACHE_INITIAL_SIZE) < 0) {
dfcc56dc6c485fe45a3fa18325f047bfaae65019Timo Sirainen mail_cache_set_syscall_error(cache, "file_set_size()");
63849db64682675a2fd3e1aea05c10ecbc6d473aTimo Sirainen (void)file_dotlock_delete(cache->filepath, fd);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (file_dotlock_replace(cache->filepath, fd, FALSE) < 0) {
9c8f854d95d8d895022a75f140a0a500eb200d39Timo Sirainen mail_cache_set_syscall_error(cache, "file_dotlock_replace()");
9c8f854d95d8d895022a75f140a0a500eb200d39Timo Sirainen if (!mmap_update(cache, 0, sizeof(struct mail_cache_header)))
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenint mail_cache_open_or_create(struct mail_index *index)
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen hdr.used_file_size = uint32_to_nbo(sizeof(hdr));
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen cache->split_header_pool = pool_alloconly_create("Headers", 512);
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* we'll do anon-mmaping only if initially requested. if we fail
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen because of out of disk space, we'll just let the main index code
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen know it and fail. */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen if (!mail_cache_open_or_create_file(cache, &hdr)) {
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen /* unset inconsistency - we already rebuilt the cache file */
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenvoid mail_cache_set_defaults(struct mail_cache *cache,
9c8f854d95d8d895022a75f140a0a500eb200d39Timo Sirainen cache->default_cache_fields = default_cache_fields;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainen cache->never_cache_fields = never_cache_fields;
d176f84ce5ca2073f4dfbafb457b9c74f6bf0d76Timo Sirainenstatic const struct mail_cache_record *
9c8f854d95d8d895022a75f140a0a500eb200d39Timo Sirainenmail_cache_compress_record(struct mail_cache *cache,
const void *data;
return data;
unsigned char *mmap_base;
const char *str;
remove_fields = 0;
used_fields = 0;
remove_fields == 0) {
t_push();
&size);
t_pop();
return TRUE;
return FALSE;
return FALSE;
return FALSE;
return FALSE;
return ret;
return TRUE;
if (ret != 0)
return ret > 0;
return FALSE;
return FALSE;
return FALSE;
return FALSE;
return FALSE;
return TRUE;
return TRUE;
int ret;
return TRUE;
return TRUE;
if (nonblock) {
if (ret < 0)
if (ret <= 0)
if (ret > 0) {
return ret;
return TRUE;
return TRUE;
return FALSE;
return TRUE;
int ret;
if (ret <= 0)
return ret;
return ret;
unsigned char lower_data;
return FALSE;
data++;
return TRUE;
data++;
return TRUE;
return FALSE;
return FALSE;
return FALSE;
return TRUE;
return FALSE;
return ret;
return TRUE;
void *base;
return FALSE;
return TRUE;
return FALSE;
return TRUE;
return offset;
unsigned char *buf;
return NULL;
return NULL;
idx);
return NULL;
if (data_size == 0) {
return NULL;
return NULL;
idx);
return NULL;
return NULL;
char *str;
return NULL;
unsigned int idx)
const char *str;
t_push();
for (i = 0; i < MAIL_CACHE_HEADERS_COUNT; i++) {
t_pop();
t_push();
headers++;
if (offset != 0) {
t_pop();
return offset > 0;
static struct mail_cache_record *
return NULL;
return NULL;
return NULL;
return NULL;
return NULL;
return NULL;
return cache_rec;
static struct mail_cache_record *
return NULL;
return next;
const void *buf;
if (write_offset == 0)
return FALSE;
return TRUE;
static struct mail_cache_record *
unsigned int idx;
return NULL;
return NULL;
return NULL;
return cache_rec;
const unsigned char *buf;
unsigned int mask;
return offset;
sizeof(data_size));
i_unreached();
return offset;
unsigned char *buf;
unsigned int idx;
int field_num;
return FALSE;
return TRUE;
return TRUE;
return TRUE;
enum mail_cache_field
return fields;
unsigned char *buf;
unsigned int mask;
if (data_size == 0) {
return FALSE;
return TRUE;
return FALSE;
i_unreached();
return FALSE;
return FALSE;
void *data;
return FALSE;
return TRUE;
const void *data;
return NULL;
return NULL;
return data;
const void *data;
return FALSE;
return TRUE;
return flags;
void *data;
return FALSE;
return TRUE;
return NULL;
return FALSE;
t_push();
t_pop();
return FALSE;