mail-index.c revision 2cd58e32ea436cb50588857cb959515d3e070056
/* Copyright (C) 2002 Timo Sirainen */
#include "lib.h"
#include "ioloop.h"
#include "hostpid.h"
#include "mmap-util.h"
#include "write-full.h"
#include "mail-index.h"
#include "mail-index-data.h"
#include "mail-index-util.h"
#include "mail-hash.h"
#include "mail-lockdir.h"
#include "mail-modifylog.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <utime.h>
{
unsigned int extra;
if (!index->dirty_mmap) {
return TRUE;
}
return FALSE;
}
return FALSE;
}
sizeof(MailIndexRecord);
if (extra != 0) {
/* partial write or corrupted -
truncate the file to valid length */
}
return TRUE;
}
{
index->set_cache_fields = 0;
}
}
}
}
}
}
}
}
{
int failed;
return FALSE;
return FALSE;
}
}
/* keep index's modify stamp same as the sync file's stamp */
return FALSE;
}
return FALSE;
}
return !failed;
}
{
return FALSE;
}
return FALSE;
}
return TRUE;
}
{
return FALSE;
return FALSE;
return TRUE;
}
{
}
if (index->set_cache_fields != 0) {
index->set_cache_fields = 0;
}
}
#define MAIL_LOCK_TO_FLOCK(lock_type) \
{
return TRUE;
/* lock whole file */
}
return FALSE;
}
return TRUE;
}
{
/* yeah, this function is a bit messy. besides locking, it keeps
the index synced and in a good shape. */
int ret;
if (index->inconsistent) {
/* index is in inconsistent state and nothing else than
free() is allowed for it. */
return FALSE;
}
return TRUE;
/* shared -> exclusive isn't allowed */
/* releasing exclusive lock */
/* sync mmaped memory */
(void)mail_index_sync_file(index);
}
if (lock_type != MAIL_LOCK_UNLOCK &&
/* unlock -> lock */
return ret;
}
/* lock whole file */
return FALSE;
}
}
if (lock_type == MAIL_LOCK_UNLOCK) {
/* reset last_lookup so rebuilds don't try to use it */
}
if (lock_type != MAIL_LOCK_UNLOCK) {
/* we're always mmap()ed when we're locked */
if (!mmap_update(index)) {
return FALSE;
}
/* index was rebuilt, there's no way we can maintain
consistency */
"%s was rebuilt while we had it open",
return FALSE;
}
} else if (old_lock_type == MAIL_LOCK_SHARED) {
/* releasing shared lock */
/* need to update the header */
}
}
if (lock_type == MAIL_LOCK_EXCLUSIVE) {
/* while holding exclusive lock, keep the FSCK flag on.
when the lock is released, the FSCK flag will also be
removed. */
return FALSE;
}
}
/* index is corrupted, rebuild it */
if (lock_type == MAIL_LOCK_SHARED)
if (!mail_index_rebuild_all(index))
return FALSE;
return ret;
}
if (lock_type == MAIL_LOCK_UNLOCK) {
/* reset header so it's not used while being unlocked */
}
return TRUE;
}
{
/* read the header */
return FALSE;
return FALSE;
/* check the compatibility */
}
/* Returns TRUE if we're compatible with given index file */
static int mail_is_compatible_index(const char *path)
{
int fd, compatible;
if (fd == -1)
return FALSE;
return compatible;
}
/* Returns a file name of compatible index */
{
struct dirent *d;
const char *name;
char path[1024];
unsigned int len;
/* first try the primary name */
if (mail_is_compatible_index(path))
return INDEX_FILE_PREFIX;
/* path doesn't exist */
return NULL;
}
/* index found, check if we're compatible */
if (mail_is_compatible_index(path)) {
break;
}
}
}
return name;
}
{
/* update \Recent message counters */
/* keep last_recent_uid to next_uid-1 */
return FALSE;
}
return FALSE;
} else {
}
/* UID values are getting too high, rebuild index */
}
return TRUE;
}
int update_recent)
{
const char *path;
/* the index file should already be checked that it exists and
we're compatible with it. */
if (fd == -1) {
return FALSE;
}
/* check the compatibility anyway just to be sure */
return FALSE;
}
do {
if (!mail_index_data_open(index)) {
break;
/* data file is corrupted, need to rebuild index */
if (!mail_index_data_create(index))
break;
}
if (!mail_hash_open_or_create(index))
break;
break;
/* index is corrupted, rebuild */
if (!mail_index_rebuild_all(index))
break;
}
/* index needs fscking */
break;
}
/* remove deleted blocks from index file */
if (!mail_index_compress(index))
break;
}
break;
}
/* need to update cached fields */
if (!mail_index_update_cache(index))
break;
}
/* remove unused space from index data file.
keep after cache_fields which may move data
and create unused space.. */
if (!mail_index_compress_data(index))
break;
}
break;
break;
} while (FALSE);
if (failed)
return !failed;
}
{
/* mark the index being rebuilt - rebuild() removes this flag
when it succeeds */
/* set the fields we always want to cache - currently nothing
except the location. many clients aren't interested about
any of the fields. */
}
int update_recent)
{
const char *path;
char index_path[1024];
*dir_unlocked = FALSE;
/* first create the index into temporary file. */
if (fd == -1)
return FALSE;
/* fill the header */
/* write header */
path);
return FALSE;
}
/* move the temp index into the real one. we also need to figure
out what to call ourself on the way. */
else {
/* fatal error */
path, index_path);
return FALSE;
}
/* fallback to index.hostname - we require each system to
have a different hostname so it's safe to override
previous index as well */
hostpid_init();
"-%s", my_hostname);
path, index_path);
return FALSE;
}
/* FIXME: race condition here! index may be opened before
it's rebuilt. maybe set it locked here, and make it require
shared lock when finding the indexes.. */
}
/* lock the index file and unlock the directory */
return FALSE;
}
*dir_unlocked = TRUE;
/* create the data file, build the index and hash */
return FALSE;
}
return FALSE;
}
/* unlock finally */
return FALSE;
}
return TRUE;
}
{
const char *name;
return FALSE;
return FALSE;
return TRUE;
}
{
const char *name;
int failed, dir_unlocked;
/* first see if it's already there */
return TRUE;
}
/* index wasn't found or it was broken. get exclusive lock and check
again, just to make sure we don't end up having two index files
due to race condition with another process. */
return FALSE;
} else {
}
return FALSE;
if (failed)
return FALSE;
return TRUE;
}
{
unsigned int max_records, first_records;
if (hdr->first_hole_position == 0)
return TRUE;
/* make sure position is valid */
sizeof(MailIndexHeader)) % sizeof(MailIndexRecord) != 0) {
"first_hole_position contains invalid value",
return FALSE;
}
/* make sure position is in range.. */
"first_hole_position points outside file",
return FALSE;
}
/* and finally check that first_hole_records is in valid range */
sizeof(MailIndexHeader)) / sizeof(MailIndexRecord);
"first_hole_records points outside file",
return FALSE;
}
return TRUE;
}
unsigned int lookup_seq)
{
unsigned int seq;
/* wanted the same record as last time */
return index->last_lookup;
}
/* out of range */
return NULL;
}
return NULL;
seekpos = sizeof(MailIndexHeader) +
/* out of range */
return NULL;
}
sizeof(MailIndexHeader));
index->mmap_length -
sizeof(MailIndexRecord));
if (hdr->first_hole_position == 0 ||
/* easy, it's just at the expected index */
"first_hole_position wasn't updated "
return NULL;
}
return rec;
}
/* we need to walk through the index to get to wanted position */
/* we want to lookup data after last lookup -
this helps us some */
} else {
/* some mails are deleted, jump after the first known hole
and start counting non-deleted messages.. */
}
seq++;
rec++;
}
return rec;
}
{
}
{
if (!mmap_update(index))
return NULL;
return index->last_lookup;
}
{
return NULL;
/* go to the next non-deleted record */
index->mmap_length);
return rec;
}
return NULL;
}
unsigned int first_uid,
unsigned int last_uid)
{
unsigned int uid, last_try_uid;
if (!mmap_update(index))
return NULL;
/* try the few first with hash lookups */
if (pos != 0) {
return (MailIndexRecord *)
}
}
if (last_try_uid == last_uid)
return NULL;
/* fallback to looking through the whole index - this shouldn't be
needed often, so don't bother trying anything too fancy. */
sizeof(MailIndexHeader));
index->mmap_length);
return NULL;
return rec;
}
rec++;
}
return NULL;
}
{
/* first check if the field even could be in the file */
/* no, but make sure the future records will have it.
we don't immediately mark the index to cache this
field for old messages as some clients never ask
the info again */
} else {
/* this is at least the second time it's being asked,
make sure it'll be cached soon. */
}
return NULL;
}
/* corrupted, the field should have been there */
"Field not found from data file",
return NULL;
}
/* index is corrupted, it will be rebuilt */
return NULL;
}
}
{
unsigned int seq;
/* same as last lookup sequence - too easy */
return index->last_lookup_seq;
}
/* easy, it's just at the expected index */
return INDEX_POSITION_INDEX(
}
return 0;
/* record before first hole */
return INDEX_POSITION_INDEX(
}
/* we know the sequence after the first hole - skip to there and
start browsing the records until ours is found */
seq++;
}
return seq;
}
{
/* unseen -> seen */
/* seen -> unseen */
/* this is the first unseen message */
else
} else if ((old_flags & MAIL_DELETED) == 0 &&
(new_flags & MAIL_DELETED)) {
/* undeleted -> deleted */
/* this is the first deleted message */
} else if ((old_flags & MAIL_DELETED) &&
(new_flags & MAIL_DELETED) == 0) {
/* deleted -> undeleted */
else
}
}
{
/* see if first_hole_records can be grown */
index->mmap_length);
rec++;
}
}
{
/* truncate index file */
return FALSE;
/* update header */
/* all mail was deleted, truncate data file */
return FALSE;
}
if (!mmap_update(index))
return FALSE;
return TRUE;
}
unsigned int seq, int external_change)
{
if (seq != 0) {
return FALSE;
}
/* expunge() may be called while index is being rebuilt and when
there's no hash yet */
/* setting UID to 0 is enough for deleting the mail from index */
/* update last_lookup_seq */
if (seq != 0) {
/* note that last_lookup can be left to point to
invalid record so that next() works properly */
index->last_lookup_seq--;
}
return FALSE;
/* update first hole */
/* first deleted message in index */
/* deleted the previous record before hole */
} else if (hdr->first_hole_position +
/* deleted the next record after hole */
} else {
/* second hole coming to index file, the index now needs to
be compressed to keep high performance */
/* new hole before the old hole */
}
}
/* update message counts */
if (hdr->messages_count == 0) {
/* corrupted */
"Header says there's no mail while expunging",
return FALSE;
}
hdr->messages_count--;
/* the hole reaches end of file, truncate it */
(void)mail_index_truncate(index);
} else {
/* update deleted_space in data file */
}
return TRUE;
}
int external_change)
{
return TRUE; /* no changes */
}
{
if (pos < 0) {
return FALSE;
}
return FALSE;
}
if (!mmap_update(index))
return FALSE;
return TRUE;
}
{
}
{
return index->inconsistent;
}