mail-hash.c revision dec85d9856c33f427a06dda01e0e50de0bc8fa7d
/* Copyright (c) 2006-2007 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "array.h"
#include "primes.h"
#include "nfs-workarounds.h"
#include "file-dotlock.h"
#include "file-set-size.h"
#include "read-full.h"
#include "write-full.h"
#include "mmap-util.h"
#include "nfs-workarounds.h"
#include "mail-index-private.h"
#include "mail-hash.h"
#include <stdio.h>
#include <stddef.h>
#include <utime.h>
/* How large to create the file initially */
#define FILE_SIZE_INIT_PERCENTAGE 120
/* How much larger to grow the file when it needs to be done */
#define MAIL_HASH_GROW_PERCENTAGE 20
/* Minimum hash size to use */
#define MAIL_HASH_MIN_SIZE 109
#define MAIL_HASH_TIMEOUT_SECS 3
struct mail_hash {
struct mail_index *index;
void *cb_context;
char *filepath;
char *suffix;
int fd;
unsigned int record_size;
void *mmap_base;
int lock_type;
struct dotlock_settings dotlock_settings;
struct mail_hash_header *hdr;
void *records_base;
unsigned int records_mapped;
unsigned int mmap_anon:1;
unsigned int in_memory:1;
unsigned int locked:1;
};
#define MAIL_HASH_IS_IN_MEMORY(hash) \
const struct dotlock_settings default_dotlock_settings = {
};
const char *function)
{
return;
}
"%s failed with index hash file %s: %m",
}
{
return 0;
return -1;
}
} else {
return -1;
}
}
/* mtime didn't change. have to increase it. */
return -1;
}
}
if (!set)
return 0;
}
{
}
const struct mail_hash_header *hdr)
{
/* silent rebuild */
return -1;
}
return -1;
}
return -1;
}
return -1;
}
return -1;
}
return -1;
}
return 0;
}
{
"munmap_anon()");
}
} else {
}
hash->mapped_mtime = 0;
}
}
}
{
return 0;
}
return 1;
}
{
int ret;
return -1;
}
return -1;
}
}
if (ret < 0) {
return -1;
}
if (ret == 0) {
return -1;
}
return 0;
}
{
if (hash->change_offset_end == 0) {
/* no changes done */
return 0;
}
MS_SYNC) < 0) {
return -1;
}
} else {
return -1;
}
hash->change_offset_start) < 0) {
return -1;
}
}
return -1;
}
}
/* now that the file is guaranteed to be updated, reset the
corruption marker */
return -1;
return 0;
}
{
if (hash->change_offset_end == 0) {
/* first change. mark the file corrupted while changes are
being done. */
return -1;
}
return 0;
}
{
return -1;
}
return 0;
}
}
return -1;
}
} else {
/* first read only the header. if the update counter hasn't
changed we don't need to read the whole file */
return -1;
if (full)
} else {
}
}
return mail_hash_file_map_finish(hash);
}
{
} else {
return -1;
}
return 0;
}
}
{
else
}
{
int ret;
return 0;
return -1;
}
if (!lock) {
return -1;
} else {
return -1;
if (ret <= 0)
}
return ret;
}
static void
{
/* note that since the index may not have been synced yet, the
uid_validity may be 0 */
if (initial_count == 0)
FILE_SIZE_INIT_PERCENTAGE / 100);
}
static int
{
struct mail_hash_header hdr;
int fd;
if (fd == -1) {
return -1;
}
(void)file_dotlock_delete(&dotlock);
return -1;
}
if (file_dotlock_replace(&dotlock, 0) < 0) {
return -1;
}
return 0;
}
unsigned int initial_count)
{
struct mail_hash_header hdr;
if (mail_hash_file_map_finish(hash) <= 0)
i_unreached();
}
struct mail_hash *
unsigned int initial_count,
void *context)
{
int ret;
i_strdup("(in-memory hash)") :
/* we don't want to create the hash */
return NULL;
}
if (ret == 0) {
/* not found or broken, recreate it */
}
if (ret < 0) {
/* fallback to in-memory hash */
}
return hash;
}
{
}
{
int ret;
if (ret == 0) {
/* should work now, try opening again */
if (ret == 0) {
"Newly created hash file broken");
return -1;
}
}
return ret < 0 ? -1 : 0;
}
{
int ret;
return -1;
if (ret > 0)
return 0;
/* not found or broken, recreate it */
return mail_hash_reset(hash, 0);
}
{
return mail_hash_reopen(hash);
return mail_hash_reopen(hash);
return -1;
}
/* if ESTALE is returned, it most likely means it was rebuilt */
} else {
return 0;
}
return mail_hash_reopen(hash);
}
{
int ret;
if (!MAIL_HASH_IS_IN_MEMORY(hash)) {
if (mail_hash_reopen_if_needed(hash) < 0)
return -1;
return ret;
return -1;
}
}
}
return 1;
}
{
if (MAIL_HASH_IS_IN_MEMORY(hash))
return;
(void)mail_hash_file_write_changes(hash);
}
}
{
}
{
const struct mail_hash_record *rec;
unsigned int hash_idx;
"Index points outside file");
return -1;
}
return 1;
}
return -1;
}
}
return 0;
}
{
if (!had_uid) {
"Too high message_count");
return -1;
}
}
} else {
if (had_uid) {
"Too low message_count");
return -1;
}
}
}
return 0;
}
{
unsigned int message_count;
/* if lots of messages have been added, the grow percentage
may not be enough. */
}
if (!MAIL_HASH_IS_IN_MEMORY(hash)) {
return -1;
}
}
if (MAIL_HASH_IS_IN_MEMORY(hash)) {
return -1;
}
if (mail_hash_file_map_finish(hash) <= 0)
return -1;
} else {
/* write the existing changes to the file and re-mmap it */
MS_SYNC) < 0) {
return -1;
}
/* reset the change offsets since we've updated the file, but
since the corrupted-flag is still set don't set
change_offset_end=0 */
hash->change_offset_start = 0;
return -1;
}
return 0;
}
{
struct mail_hash_record *rec;
/* allocate the record from the first hole */
return -1;
} else {
if (mail_hash_grow_file(hash) < 0)
return -1;
}
return -1;
}
return -1;
if (hash_key != 0) {
while (*idx_p != 0) {
return -1;
}
}
return -1;
}
return 0;
}
{
}
{
}
{
unsigned int hash_idx;
"Index points outside file");
return -1;
}
if (idx != 0) {
break;
} else {
hash->cb_context))
break;
}
}
if (idx == 0) {
"Tried to remove non-existing key");
return -1;
}
return -1;
}
return -1;
}
}
if (hash_key != 0) {
return -1;
}
}
} else {
return -1;
}
return 0;
}
{
unsigned int idx;
return idx;
}
const void **value_r)
{
struct mail_hash_record *rec;
return -1;
}
return 0;
}
const void *value)
{
struct mail_hash_record *rec;
bool had_uid;
return -1;
}
return -1;
}
void *context)
{
const struct mail_hash_record *rec;
const char *tmp_filename;
float nodes_per_list;
unsigned int map_size;
int ret = 0;
if (MAIL_HASH_IS_IN_MEMORY(hash))
return 0;
return 0;
/* create a temporary hash */
hash->cb_context);
return -1;
/* populate */
first_changed_idx = 0;
continue;
&new_idx) < 0) {
ret = -1;
break;
}
/* @UNSAFE: keep old -> new idx mapping */
}
if (ret == 0 && first_changed_idx != 0) {
ret = -1;
}
(void)mail_hash_file_write_changes(tmp_hash);
if (ret < 0) {
(void)unlink(tmp_filename);
return -1;
}
/* replace the old */
(void)unlink(tmp_filename);
return -1;
}
/* reopen the hash */
return -1;
if (ret == 0) {
"Newly created hash file broken");
return -1;
}
return 0;
}