mail-cache.c revision 7cb128dc4cae2a03a742f63ba7afee23c78e3af0
/* Copyright (c) 2003-2015 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "buffer.h"
#include "hash.h"
#include "nfs-workarounds.h"
#include "file-cache.h"
#include "mmap-util.h"
#include "read-full.h"
#include "write-full.h"
#include "mail-cache-private.h"
#include <unistd.h>
#define MAIL_CACHE_MIN_HEADER_READ_SIZE 4096
const char *function)
{
function);
}
{
}
{
/* mark the cache as unusable */
}
{
T_BEGIN {
"Corrupted index cache file %s: %s",
} T_END;
}
{
}
cache->mmap_length = 0;
}
}
{
} else if (!ESTALE_FSTAT(errno)) {
}
}
{
const void *data;
return 0;
cache->need_compress_file_seq = 0;
return 0;
}
return -1;
}
return -1;
return 1;
}
{
if (MAIL_CACHE_IS_UNUSABLE(cache)) {
if (cache->need_compress_file_seq != 0) {
/* we're waiting for compression */
return FALSE;
}
/* disabled */
return FALSE;
}
}
return TRUE;
/* see if the file has changed */
}
return TRUE;
}
/* file changed */
return TRUE;
}
/* if the old file has been deleted, the new file may have
the same inode as the old one. we'll catch this here by
checking if fstat() fails with ESTALE */
if (ESTALE_FSTAT(errno))
return TRUE;
return FALSE;
}
}
return FALSE;
}
{
struct mail_index_view *view;
const struct mail_index_ext *ext;
if (mail_cache_try_open(cache) <= 0)
return -1;
if (mail_cache_header_fields_read(cache) < 0)
return -1;
/* still different - maybe a race condition or maybe the
file_seq really is corrupted. either way, this shouldn't
happen often so we'll just mark cache to be compressed
later which fixes this. */
return 0;
}
return 1;
}
{
if (!mail_cache_need_reopen(cache)) {
/* reopening does no good */
return 0;
}
return mail_cache_reopen_now(cache);
}
{
unsigned int msg_count;
bool want_compress = FALSE;
if (hdr->minor_version == 0) {
/* compress to get ourself into the new header version */
return;
}
if (msg_count == 0)
records_count = 1;
/* probably not the real record_count, but hole offset that
Dovecot <=v2.1 versions used to use in this position.
we already checked that minor_version>0, but this could
happen if old Dovecot was used to access mailbox after
it had been updated. */
} else {
}
/* too many continued rows, compress */
}
/* too many deleted records, compress */
}
if (want_compress) {
if (!ESTALE_FSTAT(errno))
return;
}
}
}
const struct mail_cache_header *hdr)
{
/* check that the header is still ok */
return FALSE;
}
/* version changed - upgrade silently */
return FALSE;
}
/* architecture change - handle silently(?) */
return FALSE;
}
/* index id changed - handle silently */
return FALSE;
}
return FALSE;
}
return TRUE;
}
static int
{
if (offset == 0) {
/* verify the header validity only with offset=0. this way
we won't waste time re-verifying it all the time */
return -1;
}
}
if (!copy_hdr)
else {
sizeof(cache->hdr_ro_copy));
}
} else {
}
return 0;
return 1;
}
static int
const void **data_r)
{
const void *hdr_data;
void *data;
/* already mapped */
} else {
}
/* we can usually read the fields header after the cache
header. we need them both, so try to read them all with one
pread() call. */
}
if (ret < 0) {
cache->mmap_length = 0;
return -1;
}
}
const void **data_r)
{
const void *data;
if (size == 0)
size = sizeof(struct mail_cache_header);
/* verify offset + size before trying to allocate a huge amount of
memory due to them. note that we may be prefetching more than we
actually need, so don't fail too early. */
return -1;
}
return 0;
}
}
cache->remap_counter++;
if (cache->map_with_read)
if (ret < 0) {
/* In case of ESTALE we'll simply fail without error
messages. The caller will then just have to
fallback to generating the value itself.
We can't simply reopen the cache flie, because
using it requires also having updated file
offsets. */
return -1;
}
&cache->mmap_length);
}
/* already mapped */
return 1;
}
} else {
/* unusable, waiting for compression or
index is in memory */
return -1;
}
}
/* map the whole file */
cache->mmap_length = 0;
cache->mmap_length = 0;
return -1;
}
}
{
int ret;
if (ret > 0)
if (ret < 0) {
/* failed for some reason - doesn't really matter,
it's disabled for now. */
}
return ret;
}
{
struct mail_cache *cache;
if (!MAIL_INDEX_IS_IN_MEMORY(index) &&
return cache;
}
{
struct mail_cache *cache;
return cache;
}
{
}
{
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 ? 0 : timeout_secs,
} else {
enum dotlock_create_flags flags =
if (ret < 0) {
"file_dotlock_create()");
}
}
/* 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;
TRUE);
return 1;
}
{
else
}
static int
{
const struct mail_index_ext *ext;
const void *data;
struct mail_index_view *iview;
int i;
(void)mail_cache_open_and_verify(cache);
if (MAIL_CACHE_IS_UNUSABLE(cache) ||
return 0;
for (;;) {
return -1;
if (!mail_cache_need_reopen(cache)) {
/* locked the latest file */
break;
}
if (mail_cache_reopen_now(cache) <= 0) {
return -1;
}
/* okay, so it was just compressed. try again. */
}
/* now verify that the index reset_id matches the cache's file_seq */
for (i = 0; ; i++) {
break;
/* mismatch. try refreshing index once. if that doesn't help,
we can't use the cache. */
return 0;
}
return -1;
}
}
/* successfully locked - make sure our header is up to date */
sizeof(struct mail_cache_header));
}
(void)mail_cache_unlock(cache);
return -1;
}
return 1;
}
{
}
{
}
{
int ret = 0;
if (MAIL_CACHE_IS_UNUSABLE(cache)) {
/* we found it to be broken during the lock. just clean up. */
return -1;
}
if (cache->hdr_modified) {
ret = -1;
}
}
return ret;
}
{
return -1;
}
return 0;
}
{
if (*offset == 0) {
if (!ESTALE_FSTAT(errno))
return -1;
}
}
return -1;
}
return -1;
/* FIXME: this is updated only so that older Dovecot versions (<=v2.1)
can read this file. we can remove this later. */
return 0;
}
{
return !MAIL_CACHE_IS_UNUSABLE(cache);
}
struct mail_cache_view *
{
struct mail_cache_view *view;
return view;
}
{
}
bool update)
{
}
{
const struct mail_index_header *idx_hdr;
return 1;
&message_count)) {
/* all messages are too old */
return message_count+1;
}
return first_new_seq;
}