mail-cache.c revision dad1d7b721e80a7e6c0282ace93aef86312fa579
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2003-2012 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_cache_set_syscall_error(struct mail_cache *cache,
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen mail_index_file_set_syscall_error(cache->index, cache->filepath,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void mail_cache_unlink(struct mail_cache *cache)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_cache_reset(struct mail_cache *cache)
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen /* mark the cache as unusable */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_cache_set_corrupted(struct mail_cache *cache, const char *fmt, ...)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Corrupted index cache file %s: %s",
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainenvoid mail_cache_file_close(struct mail_cache *cache)
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen if (munmap(cache->mmap_base, cache->mmap_length) < 0)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen mail_cache_set_syscall_error(cache, "munmap()");
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen mail_cache_set_syscall_error(cache, "close()");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void mail_cache_init_file_cache(struct mail_cache *cache)
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen file_cache_set_fd(cache->file_cache, cache->fd);
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen file_cache_set_size(cache->file_cache, st.st_size);
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen mail_cache_set_syscall_error(cache, "fstat()");
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainenstatic bool mail_cache_need_reopen(struct mail_cache *cache)
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen /* we're waiting for compression */
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen /* disabled */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* see if the file has changed */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if ((cache->index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (nfs_safe_stat(cache->filepath, &st) < 0) {
68aa521f6fc7a40480a56a7b3d61c7dda19061f3Timo Sirainen mail_cache_set_syscall_error(cache, "stat()");
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen /* file changed */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if ((cache->index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* if the old file has been deleted, the new file may have
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen the same inode as the old one. we'll catch this here by
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen checking if fstat() fails with ESTALE */
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen mail_cache_set_syscall_error(cache, "fstat()");
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainenint mail_cache_reopen(struct mail_cache *cache)
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen /* reopening does no good */
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen mail_cache_set_syscall_error(cache, "open()");
56f45b3f3ae20e5c933701f4657dda5ef1916855Timo Sirainen ext = mail_index_view_get_ext(view, cache->ext_id);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (ext == NULL || cache->hdr->file_seq != ext->reset_id) {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen /* still different - maybe a race condition or maybe the
56f45b3f3ae20e5c933701f4657dda5ef1916855Timo Sirainen file_seq really is corrupted. either way, this shouldn't
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen happen often so we'll just mark cache to be compressed
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen later which fixes this. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->need_compress_file_seq = cache->hdr->file_seq;
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainenstatic void mail_cache_update_need_compress(struct mail_cache *cache)
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainen const struct mail_cache_header *hdr = cache->hdr;
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen cont_percentage = hdr->continued_record_count * 100 /
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainen (cache->index->map->rec_map->records_count == 0 ? 1 :
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen if (cont_percentage >= MAIL_CACHE_COMPRESS_CONTINUED_PERCENTAGE &&
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen hdr->used_file_size >= MAIL_CACHE_COMPRESS_MIN_SIZE) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* too many continued rows, compress */
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen cache->need_compress_file_seq = hdr->file_seq;
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainen /* see if we've reached the max. deleted space in file */
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen hdr->used_file_size >= MAIL_CACHE_COMPRESS_MIN_SIZE)
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainen cache->need_compress_file_seq = hdr->file_seq;
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainenstatic bool mail_cache_verify_header(struct mail_cache *cache,
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen /* check that the header is still ok */
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (cache->mmap_length < sizeof(struct mail_cache_header)) {
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen mail_cache_set_corrupted(cache, "File too small");
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen /* version changed - upgrade silently */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (hdr->compat_sizeof_uoff_t != sizeof(uoff_t)) {
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen /* architecture change - handle silently(?) */
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen /* index id changed - handle silently */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_set_corrupted(cache, "file_seq is 0");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* only check the header if we're locked */
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (hdr->used_file_size < sizeof(struct mail_cache_header)) {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen mail_cache_set_corrupted(cache, "used_file_size too small");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if ((hdr->used_file_size % sizeof(uint32_t)) != 0) {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen mail_cache_set_corrupted(cache, "used_file_size not aligned");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_set_corrupted(cache, "used_file_size too large");
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainenmail_cache_map_finish(struct mail_cache *cache, uoff_t offset, size_t size,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const struct mail_cache_header *hdr = hdr_data;
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen /* verify the header validity only with offset=0. this way
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen we won't waste time re-verifying it all the time */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_cache_map_with_read(struct mail_cache *cache, size_t offset, size_t size,
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen const void **data_r)
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen cache->read_offset + cache->read_buf->used >= offset+size) {
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen /* already mapped */
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen *data_r = CONST_PTR_OFFSET(cache->read_buf->data,
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen return mail_cache_map_finish(cache, offset, size, hdr_data, TRUE);
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen if (offset == 0 && size < MAIL_CACHE_MIN_HEADER_READ_SIZE) {
1171f0abf442638bac1827bb24a0b6b8eb682a82Timo Sirainen /* we can usually read the fields header after the cache
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen header. we need them both, so try to read them all with one
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen pread() call. */
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen data = buffer_append_space_unsafe(cache->read_buf, size);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen mail_cache_set_syscall_error(cache, "read()");
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen cache->mmap_length = offset + cache->read_buf->used;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainenint mail_cache_map(struct mail_cache *cache, size_t offset, size_t size,
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen const void **data_r)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return mail_cache_map_with_read(cache, offset, size, data_r);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ret = file_cache_read(cache->file_cache, offset, size);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* In case of ESTALE we'll simply fail without error
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen messages. The caller will then just have to
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen fallback to generating the value itself.
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen We can't simply reopen the cache flie, because
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen using it requires also having updated file
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen mail_cache_set_syscall_error(cache, "read()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *data_r = offset > cache->mmap_length ? NULL :
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen return mail_cache_map_finish(cache, offset, size,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* already mapped */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *data_r = CONST_PTR_OFFSET(cache->mmap_base, offset);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (munmap(cache->mmap_base, cache->mmap_length) < 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_set_syscall_error(cache, "munmap()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* unusable, waiting for compression or
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen index is in memory */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(cache->need_compress_file_seq != 0 ||
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* map the whole file */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->mmap_base = mmap_ro_file(cache->fd, &cache->mmap_length);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_set_syscall_error(cache, "mmap()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *data_r = offset > cache->mmap_length ? NULL :
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return mail_cache_map_finish(cache, offset, size,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic int mail_cache_try_open(struct mail_cache *cache)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_set_syscall_error(cache, "open()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (mail_cache_map(cache, 0, sizeof(struct mail_cache_header),
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainenint mail_cache_open_and_verify(struct mail_cache *cache)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* failed for some reason - doesn't really matter,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen it's disabled for now. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic struct mail_cache *mail_cache_alloc(struct mail_index *index)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_strconcat(index->filepath, MAIL_CACHE_FILE_SUFFIX, NULL);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->field_pool = pool_alloconly_create("Cache fields", 2048);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen hash_table_create(default_pool, cache->field_pool, 0,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen strcase_hash, (hash_cmp_callback_t *)strcasecmp);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (index->flags & MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL) != 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen I_MIN(MAIL_CACHE_LOCK_TIMEOUT, index->max_lock_timeout_secs);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->dotlock_settings.stale_timeout = MAIL_CACHE_LOCK_CHANGE_TIMEOUT;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (index->flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) != 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (cache->index->flags & MAIL_INDEX_OPEN_FLAG_SAVEONLY) != 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_index_register_expunge_handler(index, cache->ext_id, FALSE,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_index_register_sync_handler(index, cache->ext_id,
return cache;
return cache;
return cache;
unsigned int timeout_secs;
int ret;
if (ret < 0) {
if (ret <= 0)
return ret;
TRUE);
bool nonblock)
const void *data;
int i, ret;
(require_same_reset_id || i == 0)) {
ret = 0;
ret = 0;
if (ret > 0) {
sizeof(struct mail_cache_header));
return ret;
int ret = 0;
return ret;
struct mail_cache_view *
return view;
bool update)
&message_count)) {
return first_new_seq;