mail-cache.c revision dad1d7b721e80a7e6c0282ace93aef86312fa579
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2003-2012 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "array.h"
1171f0abf442638bac1827bb24a0b6b8eb682a82Timo Sirainen#include "buffer.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "hash.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "nfs-workarounds.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "file-cache.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mmap-util.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "read-full.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "write-full.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mail-cache-private.h"
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <unistd.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define MAIL_CACHE_MIN_HEADER_READ_SIZE 4096
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_cache_set_syscall_error(struct mail_cache *cache,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *function)
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen{
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen mail_index_file_set_syscall_error(cache->index, cache->filepath,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen function);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void mail_cache_unlink(struct mail_cache *cache)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!cache->index->readonly)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (void)unlink(cache->filepath);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_cache_reset(struct mail_cache *cache)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_cache_unlink(cache);
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen /* mark the cache as unusable */
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen cache->hdr = NULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_cache_set_corrupted(struct mail_cache *cache, const char *fmt, ...)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen va_list va;
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_cache_reset(cache);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen va_start(va, fmt);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen T_BEGIN {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_set_error(cache->index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Corrupted index cache file %s: %s",
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen cache->filepath,
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen t_strdup_vprintf(fmt, va));
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen } T_END;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen va_end(va);
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen}
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainenvoid mail_cache_file_close(struct mail_cache *cache)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen{
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen if (cache->mmap_base != NULL) {
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen if (munmap(cache->mmap_base, cache->mmap_length) < 0)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen mail_cache_set_syscall_error(cache, "munmap()");
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen }
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (cache->file_cache != NULL)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen file_cache_set_fd(cache->file_cache, -1);
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen cache->mmap_base = NULL;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen cache->hdr = NULL;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen cache->mmap_length = 0;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen cache->last_field_header_offset = 0;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (cache->file_lock != NULL)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen file_lock_free(&cache->file_lock);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen cache->locked = FALSE;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (cache->fd != -1) {
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (close(cache->fd) < 0)
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen mail_cache_set_syscall_error(cache, "close()");
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen cache->fd = -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen}
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void mail_cache_init_file_cache(struct mail_cache *cache)
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen{
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen struct stat st;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (cache->file_cache == NULL)
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen return;
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen file_cache_set_fd(cache->file_cache, cache->fd);
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (fstat(cache->fd, &st) == 0)
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen file_cache_set_size(cache->file_cache, st.st_size);
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen else if (!ESTALE_FSTAT(errno))
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen mail_cache_set_syscall_error(cache, "fstat()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->st_ino = st.st_ino;
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen cache->st_dev = st.st_dev;
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen}
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainenstatic bool mail_cache_need_reopen(struct mail_cache *cache)
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen{
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen struct stat st;
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen if (MAIL_CACHE_IS_UNUSABLE(cache)) {
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen if (cache->need_compress_file_seq != 0) {
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen /* we're waiting for compression */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return FALSE;
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen }
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(cache->index)) {
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen /* disabled */
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen return FALSE;
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen if (cache->fd == -1)
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen return TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* see if the file has changed */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if ((cache->index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(!cache->locked);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen nfs_flush_file_handle_cache(cache->filepath);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (nfs_safe_stat(cache->filepath, &st) < 0) {
68aa521f6fc7a40480a56a7b3d61c7dda19061f3Timo Sirainen mail_cache_set_syscall_error(cache, "stat()");
68aa521f6fc7a40480a56a7b3d61c7dda19061f3Timo Sirainen return TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen if (st.st_ino != cache->st_ino ||
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen !CMP_DEV_T(st.st_dev, cache->st_dev)) {
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen /* file changed */
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen return TRUE;
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
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 if (fstat(cache->fd, &st) < 0) {
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen if (ESTALE_FSTAT(errno))
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen return TRUE;
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen mail_cache_set_syscall_error(cache, "fstat()");
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen return FALSE;
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen }
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return FALSE;
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen}
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainenint mail_cache_reopen(struct mail_cache *cache)
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen{
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen struct mail_index_view *view;
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen const struct mail_index_ext *ext;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const void *data;
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen i_assert(!cache->locked);
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen if (!mail_cache_need_reopen(cache)) {
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen /* reopening does no good */
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen return 0;
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen }
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen mail_cache_file_close(cache);
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen cache->fd = nfs_safe_open(cache->filepath,
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen cache->index->readonly ? O_RDONLY : O_RDWR);
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen if (cache->fd == -1) {
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen if (errno == ENOENT)
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen cache->need_compress_file_seq = 0;
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen else
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen mail_cache_set_syscall_error(cache, "open()");
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen return -1;
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen }
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen mail_cache_init_file_cache(cache);
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen if (mail_cache_map(cache, 0, 0, &data) < 0)
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen return -1;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen if (mail_cache_header_fields_read(cache) < 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen view = mail_index_view_open(cache->index);
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;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen mail_index_view_close(&view);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_index_view_close(&view);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(!MAIL_CACHE_IS_UNUSABLE(cache));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainenstatic void mail_cache_update_need_compress(struct mail_cache *cache)
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen{
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainen const struct mail_cache_header *hdr = cache->hdr;
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen unsigned int cont_percentage;
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen uoff_t max_del_space;
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainen
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen cont_percentage = hdr->continued_record_count * 100 /
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainen (cache->index->map->rec_map->records_count == 0 ? 1 :
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen cache->index->map->rec_map->records_count);
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;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen }
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainen /* see if we've reached the max. deleted space in file */
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen max_del_space = hdr->used_file_size / 100 *
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainen MAIL_CACHE_COMPRESS_PERCENTAGE;
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen if (hdr->deleted_space >= max_del_space &&
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen hdr->used_file_size >= MAIL_CACHE_COMPRESS_MIN_SIZE)
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainen cache->need_compress_file_seq = hdr->file_seq;
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen}
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainen
58febed28f2af78b2d8a281c851d9b67160c4bd3Timo Sirainenstatic bool mail_cache_verify_header(struct mail_cache *cache,
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen const struct mail_cache_header *hdr)
2112c190034c937b4b1613c3689d8239adb6c310Timo Sirainen{
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");
785b1ca149341b70bf2cb8cc3049f1c4c1070b52Timo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (hdr->version != MAIL_CACHE_VERSION) {
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen /* version changed - upgrade silently */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_unlink(cache);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return FALSE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (hdr->compat_sizeof_uoff_t != sizeof(uoff_t)) {
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen /* architecture change - handle silently(?) */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_unlink(cache);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen return FALSE;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (hdr->indexid != cache->index->indexid) {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen /* index id changed - handle silently */
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen mail_cache_unlink(cache);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (hdr->file_seq == 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_set_corrupted(cache, "file_seq is 0");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return FALSE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* only check the header if we're locked */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!cache->locked)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return TRUE;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (hdr->used_file_size < sizeof(struct mail_cache_header)) {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen mail_cache_set_corrupted(cache, "used_file_size too small");
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen }
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 return FALSE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (cache->mmap_base != NULL &&
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen hdr->used_file_size > cache->mmap_length) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_set_corrupted(cache, "used_file_size too large");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return FALSE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return TRUE;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainenstatic int
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainenmail_cache_map_finish(struct mail_cache *cache, uoff_t offset, size_t size,
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen const void *hdr_data, bool copy_hdr)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const struct mail_cache_header *hdr = hdr_data;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen if (offset == 0) {
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 Sirainen if (!mail_cache_verify_header(cache, hdr)) {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen cache->need_compress_file_seq =
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen !MAIL_CACHE_IS_UNUSABLE(cache) &&
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen cache->hdr->file_seq != 0 ?
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->hdr->file_seq : 0;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen cache->hdr = NULL;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return -1;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen }
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen }
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (hdr_data != NULL) {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (!copy_hdr)
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen cache->hdr = hdr;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen else {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen memcpy(&cache->hdr_ro_copy, hdr,
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen sizeof(cache->hdr_ro_copy));
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen cache->hdr = &cache->hdr_ro_copy;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen }
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen mail_cache_update_need_compress(cache);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen } else {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen i_assert(cache->hdr != NULL);
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen }
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen i_assert(cache->hdr->file_seq != 0);
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (offset + size > cache->mmap_length)
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen return 0;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen return 1;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen}
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_cache_map_with_read(struct mail_cache *cache, size_t offset, size_t size,
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen const void **data_r)
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen{
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen const void *hdr_data;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen void *data;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen ssize_t ret;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen if (cache->read_buf == NULL) {
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen cache->read_buf =
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen buffer_create_dynamic(default_pool, size);
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen } else if (cache->read_offset <= offset &&
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 offset - cache->read_offset);
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen hdr_data = offset == 0 ? *data_r : NULL;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen return mail_cache_map_finish(cache, offset, size, hdr_data, TRUE);
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen } else {
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen buffer_set_used_size(cache->read_buf, 0);
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen }
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. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen size = MAIL_CACHE_MIN_HEADER_READ_SIZE;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen data = buffer_append_space_unsafe(cache->read_buf, size);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ret = pread(cache->fd, data, size, offset);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ret < 0) {
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (errno != ESTALE)
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen mail_cache_set_syscall_error(cache, "read()");
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen buffer_set_used_size(cache->read_buf, 0);
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen cache->hdr = NULL;
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen cache->mmap_length = 0;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen return -1;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen }
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen buffer_set_used_size(cache->read_buf, ret);
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen cache->read_offset = offset;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen cache->mmap_length = offset + cache->read_buf->used;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen *data_r = data;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen hdr_data = offset == 0 ? *data_r : NULL;
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen return mail_cache_map_finish(cache, offset,
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen cache->read_buf->used, hdr_data, TRUE);
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen}
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainenint mail_cache_map(struct mail_cache *cache, size_t offset, size_t size,
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen const void **data_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const void *data;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ssize_t ret;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (size == 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen size = sizeof(struct mail_cache_header);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->remap_counter++;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (cache->map_with_read)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return mail_cache_map_with_read(cache, offset, size, data_r);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen if (cache->file_cache != NULL) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ret = file_cache_read(cache->file_cache, offset, size);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ret < 0) {
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
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen We can't simply reopen the cache flie, because
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen using it requires also having updated file
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen offsets. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (errno != ESTALE)
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen mail_cache_set_syscall_error(cache, "read()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->hdr = NULL;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen data = file_cache_get_map(cache->file_cache,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen &cache->mmap_length);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *data_r = offset > cache->mmap_length ? NULL :
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen CONST_PTR_OFFSET(data, offset);
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen return mail_cache_map_finish(cache, offset, size,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen offset == 0 ? data : NULL, TRUE);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen if (offset < cache->mmap_length &&
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen size <= cache->mmap_length - offset) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* already mapped */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *data_r = CONST_PTR_OFFSET(cache->mmap_base, offset);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (cache->mmap_base != NULL) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (munmap(cache->mmap_base, cache->mmap_length) < 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_set_syscall_error(cache, "munmap()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen } else {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (cache->fd == -1) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* unusable, waiting for compression or
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen index is in memory */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(cache->need_compress_file_seq != 0 ||
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen MAIL_INDEX_IS_IN_MEMORY(cache->index));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* map the whole file */
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen cache->hdr = NULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cache->mmap_length = 0;
da5d50534cfca45d0aaaf0bdac17b287b4588809Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->mmap_base = mmap_ro_file(cache->fd, &cache->mmap_length);
da5d50534cfca45d0aaaf0bdac17b287b4588809Timo Sirainen if (cache->mmap_base == MAP_FAILED) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->mmap_base = NULL;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_set_syscall_error(cache, "mmap()");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *data_r = offset > cache->mmap_length ? NULL :
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen CONST_PTR_OFFSET(cache->mmap_base, offset);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return mail_cache_map_finish(cache, offset, size,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->mmap_base, FALSE);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic int mail_cache_try_open(struct mail_cache *cache)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const void *data;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
65a5fb2343f9870713bfca0b24abb58dcade605eTimo Sirainen cache->opened = TRUE;
65a5fb2343f9870713bfca0b24abb58dcade605eTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(cache->index))
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->fd = nfs_safe_open(cache->filepath,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->index->readonly ? O_RDONLY : O_RDWR);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (cache->fd == -1) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (errno == ENOENT) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->need_compress_file_seq = 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_set_syscall_error(cache, "open()");
65a5fb2343f9870713bfca0b24abb58dcade605eTimo Sirainen return -1;
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_init_file_cache(cache);
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (mail_cache_map(cache, 0, sizeof(struct mail_cache_header),
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen &data) < 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainenint mail_cache_open_and_verify(struct mail_cache *cache)
65a5fb2343f9870713bfca0b24abb58dcade605eTimo Sirainen{
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen int ret;
4edf90751f075cc6ab3d6f53fc78b656efa80922Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ret = mail_cache_try_open(cache);
65a5fb2343f9870713bfca0b24abb58dcade605eTimo Sirainen if (ret > 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ret = mail_cache_header_fields_read(cache);
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen if (ret < 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* failed for some reason - doesn't really matter,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen it's disabled for now. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_file_close(cache);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return ret;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic struct mail_cache *mail_cache_alloc(struct mail_index *index)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct mail_cache *cache;
a11af9df3bb29db35f956d337ab88160729580b0Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache = i_new(struct mail_cache, 1);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->index = index;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->fd = -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->filepath =
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_strconcat(index->filepath, MAIL_CACHE_FILE_SUFFIX, NULL);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->field_pool = pool_alloconly_create("Cache fields", 2048);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->field_name_hash =
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen hash_table_create(default_pool, cache->field_pool, 0,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen strcase_hash, (hash_cmp_callback_t *)strcasecmp);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->dotlock_settings.use_excl_lock =
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (index->flags & MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL) != 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->dotlock_settings.nfs_flush =
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->dotlock_settings.timeout =
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
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (!MAIL_INDEX_IS_IN_MEMORY(index) &&
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (index->flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) != 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->file_cache = file_cache_new(-1);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->map_with_read =
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (cache->index->flags & MAIL_INDEX_OPEN_FLAG_SAVEONLY) != 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen cache->ext_id =
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_index_ext_register(index, "cache", 0,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen sizeof(uint32_t), sizeof(uint32_t));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_index_register_expunge_handler(index, cache->ext_id, FALSE,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_expunge_handler, cache);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_index_register_sync_handler(index, cache->ext_id,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_cache_sync_handler,
da5d50534cfca45d0aaaf0bdac17b287b4588809Timo Sirainen MAIL_INDEX_SYNC_HANDLER_FILE |
MAIL_INDEX_SYNC_HANDLER_HEAD |
(cache->file_cache == NULL ? 0 :
MAIL_INDEX_SYNC_HANDLER_VIEW));
if (cache->file_cache != NULL) {
mail_index_register_sync_lost_handler(index,
mail_cache_sync_lost_handler);
}
return cache;
}
struct mail_cache *mail_cache_open_or_create(struct mail_index *index)
{
struct mail_cache *cache;
cache = mail_cache_alloc(index);
return cache;
}
struct mail_cache *mail_cache_create(struct mail_index *index)
{
struct mail_cache *cache;
cache = mail_cache_alloc(index);
if (!MAIL_INDEX_IS_IN_MEMORY(index)) {
if (unlink(cache->filepath) < 0 && errno != ENOENT)
mail_cache_set_syscall_error(cache, "unlink()");
}
return cache;
}
void mail_cache_free(struct mail_cache **_cache)
{
struct mail_cache *cache = *_cache;
*_cache = NULL;
if (cache->file_cache != NULL) {
mail_index_unregister_sync_lost_handler(cache->index,
mail_cache_sync_lost_handler);
file_cache_free(&cache->file_cache);
}
mail_index_unregister_expunge_handler(cache->index, cache->ext_id);
mail_index_unregister_sync_handler(cache->index, cache->ext_id);
mail_cache_file_close(cache);
if (cache->read_buf != NULL)
buffer_free(&cache->read_buf);
hash_table_destroy(&cache->field_name_hash);
pool_unref(&cache->field_pool);
i_free(cache->field_file_map);
i_free(cache->file_field_map);
i_free(cache->fields);
i_free(cache->filepath);
i_free(cache);
}
static int mail_cache_lock_file(struct mail_cache *cache, bool nonblock)
{
unsigned int timeout_secs;
int ret;
if (cache->last_lock_failed) {
/* previous locking failed. don't waste time waiting on it
again, just try once to see if it's available now. */
nonblock = TRUE;
}
if (cache->index->lock_method != FILE_LOCK_METHOD_DOTLOCK) {
i_assert(cache->file_lock == NULL);
timeout_secs = I_MIN(MAIL_CACHE_LOCK_TIMEOUT,
cache->index->max_lock_timeout_secs);
ret = mail_index_lock_fd(cache->index, cache->filepath,
cache->fd, F_WRLCK,
nonblock ? 0 : timeout_secs,
&cache->file_lock);
} else {
enum dotlock_create_flags flags =
nonblock ? DOTLOCK_CREATE_FLAG_NONBLOCK : 0;
i_assert(cache->dotlock == NULL);
ret = file_dotlock_create(&cache->dotlock_settings,
cache->filepath, flags,
&cache->dotlock);
if (ret < 0) {
mail_cache_set_syscall_error(cache,
"file_dotlock_create()");
}
}
cache->last_lock_failed = ret <= 0;
/* don't bother warning if locking failed due to a timeout. since cache
updating isn't all that important we're using a very short timeout
so it can be triggered sometimes on heavy load */
if (ret <= 0)
return ret;
mail_index_flush_read_cache(cache->index, cache->filepath, cache->fd,
TRUE);
return 1;
}
static void mail_cache_unlock_file(struct mail_cache *cache)
{
if (cache->index->lock_method != FILE_LOCK_METHOD_DOTLOCK)
file_unlock(&cache->file_lock);
else
(void)file_dotlock_delete(&cache->dotlock);
}
static int
mail_cache_lock_full(struct mail_cache *cache, bool require_same_reset_id,
bool nonblock)
{
const struct mail_index_ext *ext;
const void *data;
struct mail_index_view *iview;
uint32_t reset_id;
int i, ret;
i_assert(!cache->locked);
if (!cache->opened)
(void)mail_cache_open_and_verify(cache);
if (MAIL_CACHE_IS_UNUSABLE(cache) ||
MAIL_INDEX_IS_IN_MEMORY(cache->index) ||
cache->index->readonly)
return 0;
iview = mail_index_view_open(cache->index);
ext = mail_index_view_get_ext(iview, cache->ext_id);
reset_id = ext == NULL ? 0 : ext->reset_id;
mail_index_view_close(&iview);
if (ext == NULL && require_same_reset_id) {
/* cache not used */
return 0;
}
for (i = 0; i < 3; i++) {
if (cache->hdr->file_seq != reset_id &&
(require_same_reset_id || i == 0)) {
/* we want the latest cache file */
if (reset_id < cache->hdr->file_seq) {
/* either we're still waiting for index to
catch up with a cache compression, or
that catching up is never going to happen */
ret = 0;
break;
}
ret = mail_cache_reopen(cache);
if (ret < 0 || (ret == 0 && require_same_reset_id))
break;
}
if ((ret = mail_cache_lock_file(cache, nonblock)) <= 0) {
ret = -1;
break;
}
cache->locked = TRUE;
if (cache->hdr->file_seq == reset_id ||
!require_same_reset_id) {
/* got it */
break;
}
/* okay, so it was just compressed. try again. */
(void)mail_cache_unlock(cache);
ret = 0;
}
if (ret > 0) {
/* make sure our header is up to date */
if (cache->file_cache != NULL) {
file_cache_invalidate(cache->file_cache, 0,
sizeof(struct mail_cache_header));
}
if (mail_cache_map(cache, 0, 0, &data) > 0)
cache->hdr_copy = *cache->hdr;
else {
(void)mail_cache_unlock(cache);
ret = -1;
}
}
i_assert((ret <= 0 && !cache->locked) || (ret > 0 && cache->locked));
return ret;
}
int mail_cache_lock(struct mail_cache *cache, bool require_same_reset_id)
{
return mail_cache_lock_full(cache, require_same_reset_id, FALSE);
}
int mail_cache_try_lock(struct mail_cache *cache)
{
return mail_cache_lock_full(cache, FALSE, TRUE);
}
int mail_cache_unlock(struct mail_cache *cache)
{
int ret = 0;
i_assert(cache->locked);
if (cache->field_header_write_pending)
ret = mail_cache_header_fields_update(cache);
cache->locked = FALSE;
if (MAIL_CACHE_IS_UNUSABLE(cache)) {
/* we found it to be broken during the lock. just clean up. */
cache->hdr_modified = FALSE;
return -1;
}
if (cache->hdr_modified) {
cache->hdr_modified = FALSE;
if (mail_cache_write(cache, &cache->hdr_copy,
sizeof(cache->hdr_copy), 0) < 0)
ret = -1;
cache->hdr_ro_copy = cache->hdr_copy;
mail_cache_update_need_compress(cache);
}
if (cache->index->fsync_mode == FSYNC_MODE_ALWAYS) {
if (fdatasync(cache->fd) < 0)
mail_cache_set_syscall_error(cache, "fdatasync()");
}
mail_cache_unlock_file(cache);
return ret;
}
int mail_cache_write(struct mail_cache *cache, const void *data, size_t size,
uoff_t offset)
{
if (pwrite_full(cache->fd, data, size, offset) < 0) {
mail_cache_set_syscall_error(cache, "pwrite_full()");
return -1;
}
if (cache->file_cache != NULL)
file_cache_write(cache->file_cache, data, size, offset);
if (cache->read_buf != NULL)
buffer_set_used_size(cache->read_buf, 0);
return 0;
}
bool mail_cache_exists(struct mail_cache *cache)
{
return !MAIL_CACHE_IS_UNUSABLE(cache);
}
struct mail_cache_view *
mail_cache_view_open(struct mail_cache *cache, struct mail_index_view *iview)
{
struct mail_cache_view *view;
view = i_new(struct mail_cache_view, 1);
view->cache = cache;
view->view = iview;
view->cached_exists_buf =
buffer_create_dynamic(default_pool,
cache->file_fields_count + 10);
return view;
}
void mail_cache_view_close(struct mail_cache_view **_view)
{
struct mail_cache_view *view = *_view;
i_assert(view->trans_view == NULL);
*_view = NULL;
if (view->cache->field_header_write_pending &&
!view->cache->compressing)
(void)mail_cache_header_fields_update(view->cache);
buffer_free(&view->cached_exists_buf);
i_free(view);
}
void mail_cache_view_update_cache_decisions(struct mail_cache_view *view,
bool update)
{
view->no_decision_updates = !update;
}
uint32_t mail_cache_get_first_new_seq(struct mail_index_view *view)
{
const struct mail_index_header *idx_hdr;
uint32_t first_new_seq, message_count;
idx_hdr = mail_index_get_header(view);
if (idx_hdr->day_first_uid[7] == 0)
return 1;
if (!mail_index_lookup_seq_range(view, idx_hdr->day_first_uid[7],
(uint32_t)-1, &first_new_seq,
&message_count)) {
/* all messages are too old */
return message_count+1;
}
return first_new_seq;
}