mail-index.c revision 1969b0cbe66efb9a81ec2404f4903d7844e63729
/* 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 <utime.h>
static const char *index_file_prefixes[] =
{
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 */
}
index->last_lookup_seq = 0;
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 */
index->last_lookup_seq = 0;
}
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 */
index->last_lookup_seq = 0;
}
return TRUE;
}
static int delete_index(const char *path)
{
char tmp[1024];
int i;
/* main index */
return FALSE;
for (i = 0; index_file_prefixes[i] != NULL; i++) {
path, index_file_prefixes[i]);
return FALSE;
i++;
}
return TRUE;
}
int check_version)
{
/* read the header */
return FALSE;
return FALSE;
/* check the compatibility */
}
/* Returns TRUE if we're compatible with given index file. May delete the
file if it's from older version. */
{
int fd, compatible;
if (fd == -1) {
return FALSE;
}
/* version mismatch */
compatible = FALSE;
/* of older version, we don't need it anymore */
(void)delete_index(path);
}
}
return compatible;
}
/* Returns a file name of compatible index */
{
const char *name;
char path[1024];
hostpid_init();
/* first try .imap.index-<hostname> */
return name;
/* then try the generic .imap.index */
return name;
return NULL;
}
{
/* 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 */
}
/* finally reset the modify log marks, fsck or syncing might
have deleted some messages, and since we're only just
opening the index, there's no need to remember them */
return FALSE;
}
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;
}
/* index is corrupted, rebuild */
break;
}
if (!mail_hash_open_or_create(index))
break;
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;
}
/* don't try to support different architectures,
just overwrite the index if it's already there. */
} else {
/* fallback to .imap.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;
return NULL;
index->mmap_length);
/* check if first_uid is the first UID in the index, or an UID
before that. this is quite common and hash lookup would be
useless to try with those nonexisting old UIDs.. */
sizeof(MailIndexHeader));
} else {
sizeof(MailIndexRecord));
}
/* no messages in index */
return NULL;
}
/* yes, first_uid pointed to beginning of index.
make sure last_uid is in that range too. */
}
/* 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));
return NULL;
return rec;
}
rec++;
}
return NULL;
}
static MailIndexDataRecord *
{
/* 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;
}
return datarec;
}
{
return NULL;
/* index is corrupted, it will be rebuilt */
return NULL;
}
}
{
*size = 0;
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)
{
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;
}