mail-hash.c revision 18baaafa4757ea6feefbe544474dbf10a68961a6
/* Copyright (C) 2002 Timo Sirainen */
#include "lib.h"
#include "ioloop.h"
#include "file-set-size.h"
#include "primes.h"
#include "mmap-util.h"
#include "write-full.h"
#include "mail-index.h"
#include "mail-index-util.h"
#include "mail-hash.h"
#include <stdio.h>
#include <fcntl.h>
/* Minimum record count for a hash file. By default, the hash file size is
the number of messages * 3, and it's rebuilt after the file is 3/4 full.
Use only primes as hash file sizes. */
#define MIN_HASH_SIZE 109
/* Maximum record count for a hash file. */
#define MAX_HASH_SIZE \
/* When rebuilding hash, make it 30% full */
#define MIN_PERCENTAGE 30
/* Try rebuilding hash sometimes soon after it's 60% full */
#define REBUILD_PERCENTAGE 60
/* Force a rebuild when hash is 80% full */
#define FORCED_REBUILD_PERCENTAGE 80
/* our hashing function is simple - UID*2. The *2 is there because UIDs are
normally contiguous, so once the UIDs wrap around, we don't want to go
through lots of records just to find an empty spot */
#define HASH_FILE_SIZE(records) \
struct _MailHash {
unsigned int size;
int fd;
char *filepath;
void *mmap_base;
unsigned int anon_mmap:1;
unsigned int dirty_mmap:1;
unsigned int modified:1;
};
{
return FALSE;
}
{
}
{
return mail_hash_rebuild(hash);
}
return TRUE;
}
{
}
return FALSE;
}
return TRUE;
}
{
sizeof(MailHashRecord) != 0) {
/* hash must be corrupted, rebuilding should have noticed
if it was only partially written. */
return FALSE;
}
sizeof(MailHashRecord);
/* invalid size, probably corrupted. */
return FALSE;
}
return TRUE;
}
{
/* see if it's been rebuilt */
return TRUE;
/* index was just rebuilt. we should have noticed
this before at index->set_lock() though. */
"%s was rebuilt while we had it open",
return FALSE;
}
if (!mail_hash_file_open(hash))
return FALSE;
}
{
return;
} else {
}
}
{
return hash;
}
{
}
{
int failed;
if (!mail_hash_file_open(hash))
return FALSE;
if (!mmap_update_real(hash)) {
/* mmap() failure is fatal */
return FALSE;
}
/* make sure the header looks fine */
if (!hash_verify_header(hash))
else {
if (failed) {
"IndexID mismatch for hash file %s",
}
}
if (failed) {
/* recreate it */
return mail_hash_rebuild(hash);
}
return TRUE;
}
{
}
{
return TRUE;
return TRUE;
}
unsigned int hash_size)
{
unsigned int i, count;
/* we have empty hash file mmap()ed now. fill it by reading the
messages from index. */
}
/* mark this as an error but don't fail because of it. */
"hash file - %u found, header says %u",
}
/* setup header */
}
unsigned int hash_size)
{
void *mmap_base;
int failed;
/* fill the file with zeros */
return FALSE;
}
/* now, mmap() it */
if (mmap_base == MAP_FAILED)
}
/* we don't want to leave partially written hash around */
}
}
return !failed;
}
/* set indexid to 0 in hash file */
{
/* see if we can open it */
return TRUE;
}
return TRUE;
}
{
const char *path;
unsigned int hash_size;
return FALSE;
/* figure out size for our hash */
if (hash_size < MIN_HASH_SIZE)
hash_size > MAX_HASH_SIZE) {
/* either our calculation overflowed, or we reached the
max. value primes_closest() gave us. and there's more
mails - very unlikely. */
return FALSE;
}
/* out of disk space - don't even try building it to file */
fd = -1;
} else {
/* build the hash in a temp file, renaming it to the real hash
once finished */
}
if (fd != -1) {
if (!failed)
}
if (failed) {
fd = -1;
}
}
if (fd == -1) {
/* building hash to file failed. if it was because there
was no space in disk, we could just as well keep it in
memory */
return FALSE;
/* make sure it doesn't exist anymore */
}
/* switch fds */
return TRUE;
}
{
if (!mmap_update(hash))
return 0;
sizeof(MailHashHeader));
/* check from hash index to end of file */
/* empty hash record - not found. */
return 0;
}
}
/* check from beginning of file to hash index */
/* empty hash record - not found. */
return 0;
}
}
/* checked through the whole hash file. this really shouldn't happen,
we should have rebuilt it long time ago.. */
return 0;
}
{
sizeof(MailHashHeader));
/* check from hash index to end of file */
}
/* check from beginning of file to hash index */
}
/* hash file is full */
return NULL;
}
{
unsigned int max_used;
if (!mmap_update(hash))
return;
/* we really need a rebuild. */
if (!mail_hash_rebuild(hash))
return;
}
/* place the hash into first free record after wanted position */
/* this should never happen, we had already checked that
at least 1/5 of hash was empty. except, if the header
contained invalid record count for some reason. rebuild.. */
i_error("Hash file was 100%% full, rebuilding");
if (!mail_hash_rebuild(hash))
return;
}
if (pos != 0) {
/* update records count, and see if hash is
getting full */
}
}
} else {
/* delete record */
}
}