mail-cache.c revision cf3bea6d9b57f8608bec22d98ad547a507b05f66
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (c) 2003-2007 Dovecot authors, see the included COPYING file */
fc71e94957d0c2959a609450a2f303640d681858Sascha Wildevoid mail_cache_set_syscall_error(struct mail_cache *cache,
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde "%s failed with index cache file %s: %m",
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenvoid mail_cache_set_corrupted(struct mail_cache *cache, const char *fmt, ...)
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde /* mark the cache as unusable */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen mail_index_set_error(cache->index, "Corrupted index cache file %s: %s",
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenvoid mail_cache_file_close(struct mail_cache *cache)
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde if (munmap(cache->mmap_base, cache->mmap_length) < 0)
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde mail_cache_set_syscall_error(cache, "munmap()");
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen mail_cache_set_syscall_error(cache, "close()");
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainenstatic void mail_cache_init_file_cache(struct mail_cache *cache)
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde nfs_flush_attr_cache_fd(cache->filepath, cache->fd);
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde nfs_flush_read_cache(cache->filepath, cache->fd,
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde file_cache_set_fd(cache->file_cache, cache->fd);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen mail_cache_set_syscall_error(cache, "fstat()");
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen file_cache_set_size(cache->file_cache, st.st_size);
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstatic bool mail_cache_need_reopen(struct mail_cache *cache)
1c6dd898551d7d4d61970b24a8372438f6b72f97Timo Sirainen /* we're waiting for compression */
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde /* disabled */
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen /* see if the file has changed */
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen mail_cache_set_syscall_error(cache, "fstat()");
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen mail_cache_set_syscall_error(cache, "stat()");
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenint mail_cache_reopen(struct mail_cache *cache)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* reopening does no good */
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen mail_cache_set_syscall_error(cache, "open()");
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen ext = mail_index_view_get_ext(view, cache->ext_id);
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen if (ext == NULL || cache->hdr->file_seq != ext->reset_id) {
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen /* still different - maybe a race condition or maybe the
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen file_seq really is corrupted. either way, this shouldn't
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen happen often so we'll just mark cache to be compressed
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainen later which fixes this. */
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainen cache->need_compress_file_seq = cache->hdr->file_seq;
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainenstatic bool mail_cache_verify_header(struct mail_cache *cache)
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainen const struct mail_cache_header *hdr = cache->data;
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainen /* check that the header is still ok */
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainen if (cache->mmap_length < sizeof(struct mail_cache_header)) {
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainen mail_cache_set_corrupted(cache, "File too small");
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainen /* version changed - upgrade silently */
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainen if (hdr->compat_sizeof_uoff_t != sizeof(uoff_t)) {
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainen /* architecture change - handle silently(?) */
61767c7537efe3eb51f80d37cda12d69c67f3c05Timo Sirainen /* index id changed - handle silently */
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen mail_cache_set_corrupted(cache, "file_seq is 0");
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen /* only check the header if we're locked */
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen if (hdr->used_file_size < sizeof(struct mail_cache_header)) {
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen mail_cache_set_corrupted(cache, "used_file_size too small");
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen if ((hdr->used_file_size % sizeof(uint32_t)) != 0) {
2ba63f475f74b2aa87f9fd9e28a6c5738deb0878Timo Sirainen mail_cache_set_corrupted(cache, "used_file_size not aligned");
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen mail_cache_set_corrupted(cache, "used_file_size too large");
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainenint mail_cache_map(struct mail_cache *cache, size_t offset, size_t size)
3c296d819c54e21ce05c3d2eeeedc79be42ac593Timo Sirainen ret = file_cache_read(cache->file_cache, offset, size);
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen /* In case of ESTALE we'll simply fail without error
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen messages. The caller will then just have to
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen fallback to generating the value itself.
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen We can't simply reopen the cache flie, because
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen using it requires also having updated file
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen mail_cache_set_syscall_error(cache, "read()");
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen cache->data = file_cache_get_map(cache->file_cache,
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen /* already mapped */
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde if (munmap(cache->mmap_base, cache->mmap_length) < 0)
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen mail_cache_set_syscall_error(cache, "munmap()");
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde /* unusable, waiting for compression or
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde index is in memory */
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen /* map the whole file */
5c0ab4cc4dff573940df683eb4b23bd6077153faTimo Sirainen cache->mmap_base = mmap_ro_file(cache->fd, &cache->mmap_length);
9bb91f1dbf7cf8cfbd2df7784101df98d59fb46dTimo Sirainenstatic int mail_cache_try_open(struct mail_cache *cache)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen mail_cache_set_syscall_error(cache, "open()");
64bfe7b4a42512971db154937905dfa2bdb9cf2cTimo Sirainen if (mail_cache_map(cache, 0, sizeof(struct mail_cache_header)) < 0)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenint mail_cache_open_and_verify(struct mail_cache *cache)
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen /* failed for some reason - doesn't really matter,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen it's disabled for now. */
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainenstatic struct mail_cache *mail_cache_alloc(struct mail_index *index)
28abd7c6e719c430b7ab83b3e5d46f12d197b9caTimo Sirainen i_strconcat(index->filepath, MAIL_CACHE_FILE_SUFFIX, NULL);
28abd7c6e719c430b7ab83b3e5d46f12d197b9caTimo Sirainen cache->field_pool = pool_alloconly_create("Cache fields", 1024);
28abd7c6e719c430b7ab83b3e5d46f12d197b9caTimo Sirainen hash_create(default_pool, cache->field_pool, 0,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen strcase_hash, (hash_cmp_callback_t *)strcasecmp);
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainen cache->dotlock_settings.use_excl_lock = index->use_excl_dotlocks;
9d2a30e39c0662498db3368dbb010e36df54b7e8Timo Sirainen cache->dotlock_settings.nfs_flush = index->nfs_flush;
9d2a30e39c0662498db3368dbb010e36df54b7e8Timo Sirainen cache->dotlock_settings.timeout = MAIL_CACHE_LOCK_TIMEOUT;
9d2a30e39c0662498db3368dbb010e36df54b7e8Timo Sirainen cache->dotlock_settings.stale_timeout = MAIL_CACHE_LOCK_CHANGE_TIMEOUT;
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen mail_index_register_expunge_handler(index, cache->ext_id, FALSE,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainen mail_index_register_sync_handler(index, cache->ext_id,
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenstruct mail_cache *mail_cache_open_or_create(struct mail_index *index)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainenstruct mail_cache *mail_cache_create(struct mail_index *index)
fc71e94957d0c2959a609450a2f303640d681858Sascha Wilde if (unlink(cache->filepath) < 0 && errno != ENOENT)
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen mail_cache_set_syscall_error(cache, "unlink()");
aba994a4e79a020b4748e0ceffc194e5a18e1d1aTimo Sirainenvoid mail_cache_free(struct mail_cache **_cache)
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainen mail_index_unregister_sync_lost_handler(cache->index,
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen mail_index_unregister_expunge_handler(cache->index, cache->ext_id);
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen mail_index_unregister_sync_handler(cache->index, cache->ext_id);
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainenvoid mail_cache_flush_read_cache(struct mail_cache *cache, bool just_locked)
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen /* Assume flock() is independent of fcntl() locks. This isn't true
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen with Linux 2.6 NFS, but with it there's no point in using flock() */
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen cache->index->lock_method == FILE_LOCK_METHOD_FCNTL) {
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen nfs_flush_read_cache(cache->filepath, cache->fd,
665e9d14c005b65d95eae0baaa471c51e5caca73Timo Sirainen nfs_flush_read_cache(cache->filepath, cache->fd,
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainenstatic int mail_cache_lock_file(struct mail_cache *cache)
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen if (cache->index->lock_method != FILE_LOCK_METHOD_DOTLOCK) {
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen ret = mail_index_lock_fd(cache->index, cache->filepath,
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen ret = file_dotlock_create(&cache->dotlock_settings,
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainenstatic void mail_cache_unlock_file(struct mail_cache *cache)
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen if (cache->index->lock_method != FILE_LOCK_METHOD_DOTLOCK)
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainenint mail_cache_lock(struct mail_cache *cache, bool require_same_reset_id)
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen ext = mail_index_view_get_ext(iview, cache->ext_id);
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen /* cache not used */
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen for (i = 0; i < 3; i++) {
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen /* we want the latest cache file */
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen /* okay, so it was just compressed. try again. */
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen /* make sure our header is up to date */
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen sizeof(struct mail_cache_header));
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen i_assert((ret <= 0 && !cache->locked) || (ret > 0 && cache->locked));
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainenstatic void mail_cache_update_need_compress(struct mail_cache *cache)
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen const struct mail_cache_header *hdr = cache->hdr;
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen cont_percentage = hdr->continued_record_count * 100 /
9f10cc61ec303351b43e54155c86699ef53cb8beTimo Sirainen (cache->index->map->rec_map->records_count == 0 ? 1 :
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen if (cont_percentage >= MAIL_CACHE_COMPRESS_CONTINUED_PERCENTAGE &&
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen hdr->used_file_size >= MAIL_CACHE_COMPRESS_MIN_SIZE) {
665e9d14c005b65d95eae0baaa471c51e5caca73Timo Sirainen /* too many continued rows, compress */
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen cache->need_compress_file_seq = hdr->file_seq;
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen /* see if we've reached the max. deleted space in file */
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen hdr->used_file_size >= MAIL_CACHE_COMPRESS_MIN_SIZE)
a3f5bd60d81b456b7e4b79b26a825b71b836a537Timo Sirainen cache->need_compress_file_seq = hdr->file_seq;
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainenint mail_cache_unlock(struct mail_cache *cache)
a43145989f87ec68754e21234e7b6d892c4a4421Timo Sirainen /* we found it to be broken during the lock. just clean up. */
a43145989f87ec68754e21234e7b6d892c4a4421Timo Sirainen mail_cache_set_syscall_error(cache, "fdatasync()");
a43145989f87ec68754e21234e7b6d892c4a4421Timo Sirainenint mail_cache_write(struct mail_cache *cache, const void *data, size_t size,
a43145989f87ec68754e21234e7b6d892c4a4421Timo Sirainen if (pwrite_full(cache->fd, data, size, offset) < 0) {
a43145989f87ec68754e21234e7b6d892c4a4421Timo Sirainen mail_cache_set_syscall_error(cache, "pwrite_full()");
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen file_cache_write(cache->file_cache, data, size, offset);
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen /* data pointer may change if file cache was grown */
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainen cache->data = file_cache_get_map(cache->file_cache,
4b1359bde7d32667197548652a4b4f540062e2acTimo Sirainenmail_cache_view_open(struct mail_cache *cache, struct mail_index_view *iview)
0f4fb03953afa4a39d5e32e9ca0527c0a84f9aeeTimo Sirainenvoid mail_cache_view_close(struct mail_cache_view *view)