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