mail-cache.c revision d9fdacd5fb3e07997e5c389739d2054f0c8441d8
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_cache_set_syscall_error(struct mail_cache *cache,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "%s failed with index cache file %s: %m",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_cache_set_corrupted(struct mail_cache *cache, const char *fmt, ...)
ad850190d946d34966a56838cfdb216e021b5b5fTimo Sirainen /* mark the cache as unusable */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_set_error(cache->index, "Corrupted index cache file %s: %s",
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainenvoid mail_cache_file_close(struct mail_cache *cache)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (munmap(cache->mmap_base, cache->mmap_length) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_cache_set_syscall_error(cache, "munmap()");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_cache_set_syscall_error(cache, "close()");
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainenint mail_cache_reopen(struct mail_cache *cache)
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen /* reopening does no good */
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen cache->fd = nfs_safe_open(cache->filepath, O_RDWR);
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen mail_cache_set_syscall_error(cache, "open()");
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen file_cache_set_fd(cache->file_cache, cache->fd);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen ext = mail_index_view_get_ext(view, cache->ext_id);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (ext == NULL || cache->hdr->file_seq != ext->reset_id) {
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen /* still different - maybe a race condition or maybe the
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen file_seq really is corrupted. either way, this shouldn't
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen happen often so we'll just mark cache to be compressed
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen later which fixes this. */
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen cache->need_compress_file_seq = cache->hdr->file_seq;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenstatic bool mail_cache_verify_header(struct mail_cache *cache)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen const struct mail_cache_header *hdr = cache->data;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* check that the header is still ok */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (cache->mmap_length < sizeof(struct mail_cache_header)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_cache_set_corrupted(cache, "File too small");
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen if (cache->hdr->version != MAIL_CACHE_VERSION) {
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen /* version changed - upgrade silently */
e192a3b1ca8ae857e7d87298ea507d32977ba570Timo Sirainen if (hdr->compat_sizeof_uoff_t != sizeof(uoff_t)) {
e192a3b1ca8ae857e7d87298ea507d32977ba570Timo Sirainen /* architecture change - handle silently(?) */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (cache->hdr->indexid != cache->index->indexid) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* index id changed */
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen mail_cache_set_corrupted(cache, "indexid changed");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* only check the header if we're locked */
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen if (hdr->used_file_size < sizeof(struct mail_cache_header)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_cache_set_corrupted(cache, "used_file_size too small");
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen if ((hdr->used_file_size % sizeof(uint32_t)) != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_cache_set_corrupted(cache, "used_file_size not aligned");
56f45b3f3ae20e5c933701f4657dda5ef1916855Timo Sirainen mail_cache_set_corrupted(cache, "used_file_size too large");
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainenint mail_cache_map(struct mail_cache *cache, size_t offset, size_t size)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen ret = file_cache_read(cache->file_cache, offset, size);
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen /* In case of ESTALE we'll simply fail without error
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen messages. The caller will then just have to
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen fallback to generating the value itself.
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen We can't simply reopen the cache flie, because
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen using it requires also having updated file
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen mail_cache_set_syscall_error(cache, "read()");
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache->data = file_cache_get_map(cache->file_cache,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (offset == 0 && !mail_cache_verify_header(cache)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* already mapped */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (munmap(cache->mmap_base, cache->mmap_length) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_cache_set_syscall_error(cache, "munmap()");
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen /* unusable, waiting for compression or
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen index is in memory */
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen i_assert(cache->need_compress_file_seq != 0 ||
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* map the whole file */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen cache->mmap_base = mmap_ro_file(cache->fd, &cache->mmap_length);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_cache_set_syscall_error(cache, "mmap()");
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainenstatic int mail_cache_try_open(struct mail_cache *cache)
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen cache->fd = nfs_safe_open(cache->filepath, O_RDWR);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_cache_set_syscall_error(cache, "open()");
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen file_cache_set_fd(cache->file_cache, cache->fd);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (mail_cache_map(cache, 0, sizeof(struct mail_cache_header)) < 0)
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainenint mail_cache_open_and_verify(struct mail_cache *cache)
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen /* failed for some reason - doesn't really matter,
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen it's disabled for now. */
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainenstatic struct mail_cache *mail_cache_alloc(struct mail_index *index)
a835194f9a9dae88528367a791cbc282589f6c01Timo Sirainen i_strconcat(index->filepath, MAIL_CACHE_FILE_SUFFIX, NULL);
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen cache->field_pool = pool_alloconly_create("Cache fields", 1024);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen hash_create(default_pool, cache->field_pool, 0,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen strcase_hash, (hash_cmp_callback_t *)strcasecmp);
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen cache->dotlock_settings.timeout = MAIL_CACHE_LOCK_TIMEOUT;
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen cache->dotlock_settings.stale_timeout = MAIL_CACHE_LOCK_CHANGE_TIMEOUT;
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen if (index->mmap_disable || index->mmap_no_write)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen mail_index_register_expunge_handler(index, cache->ext_id,
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen mail_index_register_sync_handler(index, cache->ext_id,
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainenstruct mail_cache *mail_cache_open_or_create(struct mail_index *index)
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainenstruct mail_cache *mail_cache_create(struct mail_index *index)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenvoid mail_cache_free(struct mail_cache **_cache)
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen mail_index_unregister_sync_lost_handler(cache->index,
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen mail_index_unregister_expunge_handler(cache->index, cache->ext_id);
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen mail_index_unregister_sync_handler(cache->index, cache->ext_id);
2af769daebd83719ac696a440e06f6020471cec0Timo Sirainenstatic int mail_cache_lock_file(struct mail_cache *cache, int lock_type)
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen if (cache->index->lock_method != FILE_LOCK_METHOD_DOTLOCK) {
2af769daebd83719ac696a440e06f6020471cec0Timo Sirainen return mail_index_lock_fd(cache->index, cache->filepath,
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen return file_dotlock_create(&cache->dotlock_settings,
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainenstatic void mail_cache_unlock_file(struct mail_cache *cache)
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen if (cache->index->lock_method != FILE_LOCK_METHOD_DOTLOCK)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen ext = mail_index_view_get_ext(view, cache->ext_id);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen /* cache not used */
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen /* we want the latest cache file */
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen for (i = 0; i < 3; i++) {
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen /* okay, so it was just compressed. try again. */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* make sure our header is up to date */
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen sizeof(struct mail_cache_header));
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen i_assert((ret <= 0 && !cache->locked) || (ret > 0 && cache->locked));
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenstatic void mail_cache_update_need_compress(struct mail_cache *cache)
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen const struct mail_cache_header *hdr = cache->hdr;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen cont_percentage = hdr->continued_record_count * 100 /
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (cont_percentage >= COMPRESS_CONTINUED_PERCENTAGE &&
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* too many continued rows, compress */
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen cache->need_compress_file_seq = hdr->file_seq;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* see if we've reached the max. deleted space in file */
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen max_del_space = hdr->used_file_size / 100 * COMPRESS_PERCENTAGE;
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen cache->need_compress_file_seq = hdr->file_seq;
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainenint mail_cache_unlock(struct mail_cache *cache)
62cfc346eb7b0a4fd9e1ab6edd63b98711161229Timo Sirainen /* we found it to be broken during the lock. just clean up. */
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainenint mail_cache_write(struct mail_cache *cache, const void *data, size_t size,
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen if (pwrite_full(cache->fd, data, size, offset) < 0) {
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen mail_cache_set_syscall_error(cache, "pwrite_full()");
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen file_cache_write(cache->file_cache, data, size, offset);
9566c1b4506d49778659e3dc65997f3c0399cb7eTimo Sirainen /* data/hdr pointers may change if file cache was grown */
9566c1b4506d49778659e3dc65997f3c0399cb7eTimo Sirainen cache->data = file_cache_get_map(cache->file_cache,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_cache_view_open(struct mail_cache *cache, struct mail_index_view *iview)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_cache_view_close(struct mail_cache_view *view)