mail-index.c revision 52cd00e0a3f792582d3226d092418b96166cb401
5a580c3a38ced62d4bcc95b8ac7c4f2935b5d294Timo Sirainen/* Copyright (C) 2002 Timo Sirainen */
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include "lib.h"
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include "ioloop.h"
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include "hostpid.h"
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include "mmap-util.h"
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen#include "write-full.h"
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen#include "mail-index.h"
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen#include "mail-index-data.h"
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include "mail-index-util.h"
eb5ea3f4513ff2999892b8d904551f58b74f65f9Timo Sirainen#include "mail-hash.h"
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include "mail-lockdir.h"
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include "mail-modifylog.h"
eb5ea3f4513ff2999892b8d904551f58b74f65f9Timo Sirainen
25480af2e21cf136e461ec802177f52b43154485Timo Sirainen#include <stdio.h>
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include <stdlib.h>
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include <unistd.h>
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include <fcntl.h>
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include <dirent.h>
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#include <utime.h>
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainenstatic int mmap_update(MailIndex *index)
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen{
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen unsigned int extra;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (!index->dirty_mmap) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->header = (MailIndexHeader *) index->mmap_base;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return TRUE;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (index->mmap_base != NULL)
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen (void)munmap(index->mmap_base, index->mmap_length);
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->mmap_base = mmap_rw_file(index->fd, &index->mmap_length);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (index->mmap_base == MAP_FAILED) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->mmap_base = NULL;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index_set_error(index, "index: mmap() failed with file %s: %m",
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen index->filepath);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return FALSE;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (index->mmap_length < sizeof(MailIndexHeader)) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index_set_error(index, "truncated index file %s",
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->filepath);
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen INDEX_MARK_CORRUPTED(index);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return FALSE;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
c991d8c2c0d5d6c025e24fc00cb06dd61c42456dTimo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen extra = (index->mmap_length - sizeof(MailIndexHeader)) %
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen sizeof(MailIndexRecord);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (extra != 0) {
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen /* partial write or corrupted -
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen truncate the file to valid length */
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen index->mmap_length -= extra;
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen (void)ftruncate(index->fd, (off_t)index->mmap_length);
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen }
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen
c991d8c2c0d5d6c025e24fc00cb06dd61c42456dTimo Sirainen index->last_lookup_seq = 0;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->last_lookup = NULL;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen index->header = (MailIndexHeader *) index->mmap_base;
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen index->dirty_mmap = FALSE;
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen return TRUE;
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen}
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainen
4082d5b171d1c3a00ba705093d62b8afc9cf17aeTimo Sirainenvoid mail_index_close(MailIndex *index)
c991d8c2c0d5d6c025e24fc00cb06dd61c42456dTimo Sirainen{
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->set_flags = 0;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->set_cache_fields = 0;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->opened = FALSE;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->updating = FALSE;
c991d8c2c0d5d6c025e24fc00cb06dd61c42456dTimo Sirainen index->inconsistent = FALSE;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->dirty_mmap = TRUE;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->lock_type = MAIL_LOCK_UNLOCK;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->header = NULL;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (index->fd != -1) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen (void)close(index->fd);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->fd = -1;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (index->filepath != NULL) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen i_free(index->filepath);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->filepath = NULL;
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen }
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen if (index->mmap_base != NULL) {
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen (void)munmap(index->mmap_base, index->mmap_length);
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen index->mmap_base = NULL;
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen }
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen if (index->data != NULL) {
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen mail_index_data_free(index->data);
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen index->data = NULL;
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen }
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen
d06d6667bac64aabe1efb216af56ca45108d63b0Timo Sirainen if (index->hash != NULL) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen mail_hash_free(index->hash);
c991d8c2c0d5d6c025e24fc00cb06dd61c42456dTimo Sirainen index->hash = NULL;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen if (index->modifylog != NULL) {
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen mail_modifylog_free(index->modifylog);
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen index->modifylog = NULL;
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen }
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen if (index->error != NULL) {
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen i_free(index->error);
86bde2c1838d1ce967fa2b394bb952004a4adcb7Timo Sirainen index->error = NULL;
605eca549c08af753e05c25937bcccd66079c321Timo Sirainen }
605eca549c08af753e05c25937bcccd66079c321Timo Sirainen}
605eca549c08af753e05c25937bcccd66079c321Timo Sirainen
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainenint mail_index_sync_file(MailIndex *index)
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen{
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen struct utimbuf ut;
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen int failed;
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen if (!mail_index_data_sync_file(index->data))
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen return FALSE;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen if (index->mmap_base != NULL) {
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen if (msync(index->mmap_base, index->mmap_length, MS_SYNC) == -1) {
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen index_set_error(index, "msync() failed for %s: %m",
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen index->filepath);
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen return FALSE;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen }
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen }
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen failed = FALSE;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen if (!mail_hash_sync_file(index->hash))
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen failed = TRUE;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen if (!mail_modifylog_sync_file(index->modifylog))
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen failed = TRUE;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen /* keep index's modify stamp same as the sync file's stamp */
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen ut.actime = ioloop_time;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen ut.modtime = index->file_sync_stamp;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen if (utime(index->filepath, &ut) == -1) {
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen index_set_error(index, "utime() failed for %s: %m",
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen index->filepath);
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen return FALSE;
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen }
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen if (fsync(index->fd) == -1) {
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen index_set_error(index, "fsync() failed for %s: %m",
355fe8b5d02904df39e793f66da5432d86649d4aTimo Sirainen index->filepath);
605eca549c08af753e05c25937bcccd66079c321Timo Sirainen return FALSE;
605eca549c08af753e05c25937bcccd66079c321Timo Sirainen }
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen return !failed;
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen}
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainenint mail_index_fmsync(MailIndex *index, size_t size)
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen{
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen if (msync(index->mmap_base, size, MS_SYNC) == -1) {
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen index_set_error(index, "msync() failed for %s: %m",
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen index->filepath);
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen return FALSE;
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen }
6646bd844c85d5b27451199d8868b6d2357cd293Timo Sirainen if (fsync(index->fd) == -1) {
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen index_set_error(index, "fsync() failed for %s: %m",
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->filepath);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return FALSE;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return TRUE;
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen}
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainenint mail_index_rebuild_all(MailIndex *index)
e8ecd8f24ffc612f5d0be10f7931ac619f1eab88Timo Sirainen{
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen if (!index->rebuild(index))
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return FALSE;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (!mail_hash_rebuild(index->hash))
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return FALSE;
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return TRUE;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen}
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainenstatic void mail_index_update_header_changes(MailIndex *index)
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen{
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen if (index->set_flags != 0) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->header->flags |= index->set_flags;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->set_flags = 0;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen if (index->set_cache_fields != 0) {
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen index->header->cache_fields = index->set_cache_fields;
e262f3aa3429dbc74f668bc8bd501cf08b955778Timo Sirainen index->set_cache_fields = 0;
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen}
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen#define MAIL_LOCK_TO_FLOCK(lock_type) \
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen ((lock_type) == MAIL_LOCK_UNLOCK ? F_UNLCK : \
25480af2e21cf136e461ec802177f52b43154485Timo Sirainen (lock_type) == MAIL_LOCK_SHARED ? F_RDLCK : F_WRLCK)
8bb360f9e5de1c25e4f875205bb06e8bf15dae14Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainenint mail_index_try_lock(MailIndex *index, MailLockType lock_type)
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen{
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen struct flock fl;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (index->lock_type == lock_type)
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return TRUE;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen /* lock whole file */
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen fl.l_type = MAIL_LOCK_TO_FLOCK(lock_type);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen fl.l_whence = SEEK_SET;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen fl.l_start = 0;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen fl.l_len = 0;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (fcntl(index->fd, F_SETLK, &fl) == -1) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (errno != EINTR && errno != EACCES) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index_set_error(index, "fcntl(F_SETLKW, %d) "
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen "failed for file %s: %m", fl.l_type,
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen index->filepath);
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return FALSE;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen }
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen return TRUE;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen}
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainenint mail_index_set_lock(MailIndex *index, MailLockType lock_type)
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen{
4c9a72e0988d462df49810984dc93b3fd4a24c23Timo Sirainen /* yeah, this function is a bit messy. besides locking, it keeps
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen the index synced and in a good shape. */
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen MailLockType old_lock_type;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen struct flock fl;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen int ret;
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if (index->inconsistent) {
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen /* index is in inconsistent state and nothing else than
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen free() is allowed for it. */
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen return FALSE;
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen }
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen if (index->lock_type == lock_type)
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen return TRUE;
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen /* shared -> exclusive isn't allowed */
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen i_assert(lock_type != MAIL_LOCK_EXCLUSIVE ||
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen index->lock_type != MAIL_LOCK_SHARED);
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen if (index->lock_type == MAIL_LOCK_EXCLUSIVE) {
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen /* releasing exclusive lock */
749a9676d265a517c7a731f5b9336c524a49e6a6Timo Sirainen index->header->flags &= ~MAIL_INDEX_FLAG_FSCK;
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen
749a9676d265a517c7a731f5b9336c524a49e6a6Timo Sirainen mail_index_update_header_changes(index);
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen /* sync mmaped memory */
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen (void)mail_index_sync_file(index);
5e85a6a1349177c613dea55aabb20d857b8240a5Timo Sirainen }
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen if (lock_type != MAIL_LOCK_UNLOCK &&
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen index->lock_type == MAIL_LOCK_UNLOCK && !index->updating) {
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen /* unlock -> lock */
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen index->updating = TRUE;
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen (void)index->sync(index);
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen ret = mail_index_set_lock(index, lock_type);
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen index->updating = FALSE;
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen return ret;
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen }
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen /* lock whole file */
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen fl.l_type = MAIL_LOCK_TO_FLOCK(lock_type);
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen fl.l_whence = SEEK_SET;
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen fl.l_start = 0;
ef174bf5299348e8c0662d235341869f319cfe54Timo Sirainen fl.l_len = 0;
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen
ba14267101444b8f144091cefd437e1ea44d3e32Timo Sirainen while (fcntl(index->fd, F_SETLKW, &fl) == -1) {
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen if (errno != EINTR) {
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen index_set_error(index, "fcntl(F_SETLKW, %d) "
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen "failed for file %s: %m", fl.l_type,
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen index->filepath);
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen return FALSE;
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen }
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen }
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen if (lock_type == MAIL_LOCK_UNLOCK) {
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen /* reset last_lookup so rebuilds don't try to use it */
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen index->last_lookup_seq = 0;
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen index->last_lookup = NULL;
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen }
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen old_lock_type = index->lock_type;
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen index->lock_type = lock_type;
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen if (lock_type != MAIL_LOCK_UNLOCK) {
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen /* we're always mmap()ed when we're locked */
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen if (!mmap_update(index)) {
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen (void)mail_index_set_lock(index, MAIL_LOCK_UNLOCK);
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen return FALSE;
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen }
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen if (index->indexid != index->header->indexid) {
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen /* index was rebuilt, there's no way we can maintain
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen consistency */
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen index_set_error(index, "Warning: Inconsistency - Index "
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen "%s was rebuilt while we had it open",
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen index->filepath);
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen index->inconsistent = TRUE;
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen return FALSE;
461ffead9720d1e516b959d5e41f049c73d38c7cTimo Sirainen }
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen } else if (old_lock_type == MAIL_LOCK_SHARED) {
c47e837a127c533e67debafde8ccf9691041be16Timo Sirainen /* releasing shared lock */
eb5ea3f4513ff2999892b8d904551f58b74f65f9Timo Sirainen unsigned int old_flags, old_cache;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen old_flags = index->header->flags;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen old_cache = index->header->cache_fields;
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen
503a863a317acba125a4e46435694e35fad769e4Timo Sirainen if ((old_flags | index->set_flags) != old_flags ||
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen (old_cache | index->set_cache_fields) != old_cache) {
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen /* need to update the header */
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen index->updating = TRUE;
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen if (mail_index_set_lock(index, MAIL_LOCK_EXCLUSIVE))
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen mail_index_update_header_changes(index);
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen index->updating = FALSE;
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen return mail_index_set_lock(index, MAIL_LOCK_UNLOCK);
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen }
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen }
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen if (lock_type == MAIL_LOCK_EXCLUSIVE) {
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen /* while holding exclusive lock, keep the FSCK flag on.
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen when the lock is released, the FSCK flag will also be
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen removed. */
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen index->header->flags |= MAIL_INDEX_FLAG_FSCK;
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen if (!mail_index_fmsync(index, sizeof(MailIndexHeader))) {
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen (void)mail_index_set_lock(index, MAIL_LOCK_UNLOCK);
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen return FALSE;
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen }
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen }
e4cebacdec9c9e5b685dde5f7cbf7a5cf7e1d248Timo Sirainen
9905ec03fb2011419caeac4cd5a1b6c28ab50a73Timo Sirainen if (index->header != NULL && !index->updating &&
9905ec03fb2011419caeac4cd5a1b6c28ab50a73Timo Sirainen (index->header->flags & MAIL_INDEX_FLAG_REBUILD) != 0) {
9905ec03fb2011419caeac4cd5a1b6c28ab50a73Timo Sirainen /* index is corrupted, rebuild it */
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen index->updating = TRUE;
9905ec03fb2011419caeac4cd5a1b6c28ab50a73Timo Sirainen
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen if (lock_type == MAIL_LOCK_SHARED)
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen (void)mail_index_set_lock(index, MAIL_LOCK_UNLOCK);
9905ec03fb2011419caeac4cd5a1b6c28ab50a73Timo Sirainen
9905ec03fb2011419caeac4cd5a1b6c28ab50a73Timo Sirainen if (!mail_index_rebuild_all(index))
9905ec03fb2011419caeac4cd5a1b6c28ab50a73Timo Sirainen return FALSE;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen ret = mail_index_set_lock(index, lock_type);
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen index->updating = FALSE;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen return ret;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen }
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen if (lock_type == MAIL_LOCK_UNLOCK) {
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen /* reset header so it's not used while being unlocked */
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen index->last_lookup_seq = 0;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen index->last_lookup = NULL;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen }
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen return TRUE;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen}
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainenstatic int read_and_verify_header(int fd, MailIndexHeader *hdr)
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen{
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen /* read the header */
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen if (lseek(fd, 0, SEEK_SET) != 0)
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen return FALSE;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen if (read(fd, hdr, sizeof(MailIndexHeader)) != sizeof(MailIndexHeader))
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen return FALSE;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen /* check the compatibility */
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen return hdr->compat_data[0] == MAIL_INDEX_VERSION &&
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen hdr->compat_data[1] == MAIL_INDEX_COMPAT_FLAGS &&
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen hdr->compat_data[2] == sizeof(unsigned int) &&
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen hdr->compat_data[3] == sizeof(time_t) &&
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen hdr->compat_data[4] == sizeof(uoff_t) &&
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen hdr->compat_data[5] == MEM_ALIGN_SIZE;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen}
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen/* Returns TRUE if we're compatible with given index file */
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainenstatic int mail_is_compatible_index(const char *path)
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen{
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen MailIndexHeader hdr;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen int fd, compatible;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen fd = open(path, O_RDONLY);
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen if (fd == -1)
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen return FALSE;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen compatible = read_and_verify_header(fd, &hdr);
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen (void)close(fd);
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen return compatible;
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen}
90b50df264b57e0f63cd8cc6aea1ce3bb7cf5f64Timo Sirainen
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen/* Returns a file name of compatible index */
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainenstatic const char *mail_find_index(MailIndex *index)
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen{
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen DIR *dir;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen struct dirent *d;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen const char *name;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen char path[1024];
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen unsigned int len;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen /* first try the primary name */
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen i_snprintf(path, sizeof(path), "%s/" INDEX_FILE_PREFIX, index->dir);
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen if (mail_is_compatible_index(path))
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen return INDEX_FILE_PREFIX;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen dir = opendir(index->dir);
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen if (dir == NULL) {
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen /* path doesn't exist */
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen index_set_error(index, "Can't open dir %s: %m",
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen index->dir);
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen return NULL;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen }
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen len = strlen(INDEX_FILE_PREFIX);
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen name = NULL;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen while ((d = readdir(dir)) != NULL) {
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen if (strncmp(d->d_name, INDEX_FILE_PREFIX, len) == 0) {
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen /* index found, check if we're compatible */
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen i_snprintf(path, sizeof(path), "%s/%s",
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen index->dir, d->d_name);
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen if (mail_is_compatible_index(path)) {
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen name = t_strdup(d->d_name);
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen break;
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen }
5fdeff082e329e4a85bb7e74aaec2c35e2288557Timo Sirainen }
}
(void)closedir(dir);
return name;
}
static int mail_index_open_init(MailIndex *index, int update_recent,
MailIndexHeader *hdr)
{
/* update \Recent message counters */
if (update_recent && hdr->last_nonrecent_uid != hdr->next_uid-1) {
/* keep last_recent_uid to next_uid-1 */
if (index->lock_type == MAIL_LOCK_SHARED) {
if (!index->set_lock(index, MAIL_LOCK_UNLOCK))
return FALSE;
}
if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE))
return FALSE;
index->first_recent_uid = index->header->last_nonrecent_uid+1;
index->header->last_nonrecent_uid = index->header->next_uid-1;
} else {
index->first_recent_uid = hdr->last_nonrecent_uid+1;
}
if (hdr->next_uid >= INT_MAX-1024) {
/* UID values are getting too high, rebuild index */
index->set_flags |= MAIL_INDEX_FLAG_REBUILD;
}
return TRUE;
}
static int mail_index_open_file(MailIndex *index, const char *filename,
int update_recent)
{
MailIndexHeader hdr;
const char *path;
int fd, failed;
/* the index file should already be checked that it exists and
we're compatible with it. */
path = t_strconcat(index->dir, "/", filename, NULL);
fd = open(path, O_RDWR);
if (fd == -1) {
index_set_error(index, "Can't open index %s: %m", path);
return FALSE;
}
/* check the compatibility anyway just to be sure */
if (!read_and_verify_header(fd, &hdr)) {
index_set_error(index, "Non-compatible index file %s", path);
return FALSE;
}
if (index->fd != -1)
mail_index_close(index);
index->fd = fd;
index->filepath = i_strdup(path);
index->indexid = hdr.indexid;
index->dirty_mmap = TRUE;
index->updating = TRUE;
failed = TRUE;
do {
/* open/create the index files */
if (!mail_index_data_open(index)) {
if ((index->set_flags & MAIL_INDEX_FLAG_REBUILD) == 0)
break;
/* data file is corrupted, need to rebuild index */
hdr.flags |= MAIL_INDEX_FLAG_REBUILD;
index->set_flags = 0;
if (!mail_index_data_create(index))
break;
}
if (!mail_hash_open_or_create(index))
break;
if (!mail_modifylog_open_or_create(index))
break;
if (hdr.flags & MAIL_INDEX_FLAG_REBUILD) {
/* index is corrupted, rebuild */
if (!mail_index_rebuild_all(index))
break;
}
if (hdr.flags & MAIL_INDEX_FLAG_FSCK) {
/* index needs fscking */
if (!index->fsck(index))
break;
}
if (hdr.flags & MAIL_INDEX_FLAG_COMPRESS) {
/* remove deleted blocks from index file */
if (!mail_index_compress(index))
break;
}
if (hdr.flags & MAIL_INDEX_FLAG_REBUILD_HASH) {
if (!mail_hash_rebuild(index->hash))
break;
}
if (hdr.flags & MAIL_INDEX_FLAG_CACHE_FIELDS) {
/* need to update cached fields */
if (!mail_index_update_cache(index))
break;
}
if (hdr.flags & MAIL_INDEX_FLAG_COMPRESS_DATA) {
/* 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;
}
if (!index->sync(index))
break;
if (!mail_index_open_init(index, update_recent, &hdr))
break;
failed = FALSE;
} while (FALSE);
index->updating = FALSE;
if (!index->set_lock(index, MAIL_LOCK_UNLOCK))
failed = TRUE;
if (failed)
mail_index_close(index);
return !failed;
}
void mail_index_init_header(MailIndexHeader *hdr)
{
memset(hdr, 0, sizeof(MailIndexHeader));
hdr->compat_data[0] = MAIL_INDEX_VERSION;
hdr->compat_data[1] = MAIL_INDEX_COMPAT_FLAGS;
hdr->compat_data[2] = sizeof(unsigned int);
hdr->compat_data[3] = sizeof(time_t);
hdr->compat_data[4] = sizeof(uoff_t);
hdr->compat_data[5] = MEM_ALIGN_SIZE;
hdr->indexid = ioloop_time;
/* mark the index being rebuilt - rebuild() removes this flag
when it succeeds */
hdr->flags = MAIL_INDEX_FLAG_REBUILD;
/* set the fields we always want to cache - currently nothing
except the location. many clients aren't interested about
any of the fields. */
hdr->cache_fields = FIELD_TYPE_LOCATION;
hdr->uid_validity = ioloop_time;
hdr->next_uid = 1;
}
static int mail_index_create(MailIndex *index, int *dir_unlocked,
int update_recent)
{
MailIndexHeader hdr;
const char *path;
char index_path[1024];
int fd, len;
*dir_unlocked = FALSE;
/* first create the index into temporary file. */
fd = mail_index_create_temp_file(index, &path);
if (fd == -1)
return FALSE;
/* fill the header */
mail_index_init_header(&hdr);
/* write header */
if (write_full(fd, &hdr, sizeof(hdr)) < 0) {
index_set_error(index, "Error writing to temp index %s: %m",
path);
(void)close(fd);
(void)unlink(path);
return FALSE;
}
/* move the temp index into the real one. we also need to figure
out what to call ourself on the way. */
len = i_snprintf(index_path, sizeof(index_path),
"%s/" INDEX_FILE_PREFIX, index->dir);
if (link(path, index_path) == 0)
(void)unlink(path);
else {
if (errno != EEXIST) {
/* fatal error */
index_set_error(index, "link(%s, %s) failed: %m",
path, index_path);
(void)close(fd);
(void)unlink(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();
i_snprintf(index_path + len, sizeof(index_path)-len,
"-%s", my_hostname);
if (rename(path, index_path) == -1) {
index_set_error(index, "rename(%s, %s) failed: %m",
path, index_path);
(void)close(fd);
(void)unlink(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.. */
}
if (index->fd != -1)
mail_index_close(index);
index->fd = fd;
index->filepath = i_strdup(index_path);
index->indexid = hdr.indexid;
index->updating = TRUE;
index->dirty_mmap = TRUE;
/* lock the index file and unlock the directory */
if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE)) {
index->updating = FALSE;
return FALSE;
}
if (mail_index_lock_dir(index, MAIL_LOCK_UNLOCK))
*dir_unlocked = TRUE;
/* create the data file, build the index and hash */
if (!mail_index_data_create(index) || !index->rebuild(index) ||
!mail_hash_create(index) || !mail_modifylog_create(index)) {
index->updating = FALSE;
mail_index_close(index);
return FALSE;
}
index->updating = FALSE;
if (!mail_index_open_init(index, update_recent, index->header)) {
mail_index_close(index);
return FALSE;
}
/* unlock finally */
if (!index->set_lock(index, MAIL_LOCK_UNLOCK)) {
mail_index_close(index);
return FALSE;
}
return TRUE;
}
int mail_index_open(MailIndex *index, int update_recent)
{
const char *name;
i_assert(!index->opened);
name = mail_find_index(index);
if (name == NULL)
return FALSE;
if (!mail_index_open_file(index, name, update_recent))
return FALSE;
index->opened = TRUE;
return TRUE;
}
int mail_index_open_or_create(MailIndex *index, int update_recent)
{
const char *name;
int failed, dir_unlocked;
i_assert(!index->opened);
/* first see if it's already there */
name = mail_find_index(index);
if (name != NULL && mail_index_open_file(index, name, update_recent)) {
index->opened = TRUE;
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. */
if (!mail_index_lock_dir(index, MAIL_LOCK_EXCLUSIVE))
return FALSE;
name = mail_find_index(index);
if (name == NULL || !mail_index_open_file(index, name, update_recent)) {
/* create/rebuild index */
failed = !mail_index_create(index, &dir_unlocked,
update_recent);
} else {
dir_unlocked = FALSE;
failed = FALSE;
}
if (!dir_unlocked && !mail_index_lock_dir(index, MAIL_LOCK_UNLOCK))
return FALSE;
if (failed)
return FALSE;
index->opened = TRUE;
return TRUE;
}
int mail_index_verify_hole_range(MailIndex *index)
{
MailIndexHeader *hdr;
unsigned int max_records, first_records;
hdr = index->header;
if (hdr->first_hole_position == 0)
return TRUE;
/* make sure position is valid */
if (hdr->first_hole_position < sizeof(MailIndexHeader) ||
(hdr->first_hole_position -
sizeof(MailIndexHeader)) % sizeof(MailIndexRecord) != 0) {
index_set_error(index, "Error in index file %s: "
"first_hole_position contains invalid value",
index->filepath);
INDEX_MARK_CORRUPTED(index);
return FALSE;
}
/* make sure position is in range.. */
if (hdr->first_hole_position >= index->mmap_length) {
index_set_error(index, "Error in index file %s: "
"first_hole_position points outside file",
index->filepath);
INDEX_MARK_CORRUPTED(index);
return FALSE;
}
/* and finally check that first_hole_records is in valid range */
max_records = MAIL_INDEX_RECORD_COUNT(index);
first_records = (hdr->first_hole_position -
sizeof(MailIndexHeader)) / sizeof(MailIndexRecord);
if (index->header->first_hole_records > max_records ||
first_records + index->header->first_hole_records > max_records) {
index_set_error(index, "Error in index file %s: "
"first_hole_records points outside file",
index->filepath);
INDEX_MARK_CORRUPTED(index);
return FALSE;
}
return TRUE;
}
static MailIndexRecord *mail_index_lookup_mapped(MailIndex *index,
unsigned int lookup_seq)
{
MailIndexHeader *hdr;
MailIndexRecord *rec, *last_rec;
unsigned int seq;
uoff_t seekpos;
if (lookup_seq == index->last_lookup_seq &&
index->last_lookup != NULL && index->last_lookup->uid != 0) {
/* wanted the same record as last time */
return index->last_lookup;
}
hdr = index->header;
if (lookup_seq > hdr->messages_count) {
/* out of range */
return NULL;
}
if (!mail_index_verify_hole_range(index))
return NULL;
seekpos = sizeof(MailIndexHeader) +
(uoff_t)(lookup_seq-1) * sizeof(MailIndexRecord);
if (seekpos + sizeof(MailIndexRecord) > index->mmap_length) {
/* out of range */
return NULL;
}
rec = (MailIndexRecord *) ((char *) index->mmap_base +
sizeof(MailIndexHeader));
last_rec = (MailIndexRecord *) ((char *) index->mmap_base +
index->mmap_length -
sizeof(MailIndexRecord));
if (hdr->first_hole_position == 0 ||
hdr->first_hole_position > seekpos) {
/* easy, it's just at the expected index */
rec += lookup_seq-1;
i_assert(rec <= last_rec);
if (rec->uid == 0) {
index_set_error(index, "Error in index file %s: "
"first_hole_position wasn't updated "
"properly", index->filepath);
INDEX_MARK_CORRUPTED(index);
return NULL;
}
return rec;
}
/* we need to walk through the index to get to wanted position */
if (lookup_seq > index->last_lookup_seq && index->last_lookup != NULL) {
/* we want to lookup data after last lookup -
this helps us some */
rec = index->last_lookup;
seq = index->last_lookup_seq;
} else {
/* some mails are deleted, jump after the first known hole
and start counting non-deleted messages.. */
seq = INDEX_POSITION_INDEX(hdr->first_hole_position + 1) + 1;
rec += seq-1 + hdr->first_hole_records;
}
while (seq < lookup_seq && rec <= last_rec) {
if (rec->uid != 0)
seq++;
rec++;
}
return rec;
}
MailIndexHeader *mail_index_get_header(MailIndex *index)
{
i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
return index->header;
}
MailIndexRecord *mail_index_lookup(MailIndex *index, unsigned int seq)
{
i_assert(seq > 0);
i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
if (!mmap_update(index))
return NULL;
index->last_lookup = mail_index_lookup_mapped(index, seq);
index->last_lookup_seq = seq;
return index->last_lookup;
}
MailIndexRecord *mail_index_next(MailIndex *index, MailIndexRecord *rec)
{
MailIndexRecord *end_rec;
i_assert(!index->dirty_mmap);
i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
if (rec == NULL)
return NULL;
/* go to the next non-deleted record */
end_rec = (MailIndexRecord *) ((char *) index->mmap_base +
index->mmap_length);
while (++rec < end_rec) {
if (rec->uid != 0)
return rec;
}
return NULL;
}
MailIndexRecord *mail_index_lookup_uid_range(MailIndex *index,
unsigned int first_uid,
unsigned int last_uid)
{
MailIndexRecord *rec, *end_rec;
unsigned int uid, last_try_uid;
uoff_t pos;
i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
i_assert(first_uid > 0 && last_uid > 0);
i_assert(first_uid <= last_uid);
if (!mmap_update(index))
return NULL;
/* try the few first with hash lookups */
last_try_uid = last_uid - first_uid < 10 ? last_uid : first_uid + 4;
for (uid = first_uid; uid <= last_try_uid; uid++) {
pos = mail_hash_lookup_uid(index->hash, uid);
if (pos != 0) {
return (MailIndexRecord *)
((char *) index->mmap_base + pos);
}
}
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. */
rec = (MailIndexRecord *) ((char *) index->mmap_base +
sizeof(MailIndexHeader));
end_rec = (MailIndexRecord *) ((char *) index->mmap_base +
index->mmap_length);
while (rec < end_rec) {
if (rec->uid != 0) {
if (rec->uid > last_uid)
return NULL;
if (rec->uid >= first_uid)
return rec;
}
rec++;
}
return NULL;
}
const char *mail_index_lookup_field(MailIndex *index, MailIndexRecord *rec,
MailField field)
{
MailIndexDataRecord *datarec;
i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
/* first check if the field even could be in the file */
if ((rec->cached_fields & field) != field) {
if ((index->header->cache_fields & field) == 0) {
/* 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 */
index->set_cache_fields |= field;
} else {
/* this is at least the second time it's being asked,
make sure it'll be cached soon. */
index->set_flags |= MAIL_INDEX_FLAG_CACHE_FIELDS;
}
return NULL;
}
datarec = mail_index_data_lookup(index->data, rec, field);
if (datarec == NULL) {
/* corrupted, the field should have been there */
index_set_error(index, "Error in index file %s: "
"Field not found from data file",
index->filepath);
INDEX_MARK_CORRUPTED(index);
return NULL;
}
if (!mail_index_data_record_verify(index->data, datarec)) {
/* index is corrupted, it will be rebuilt */
return NULL;
}
return datarec->data;
}
unsigned int mail_index_get_sequence(MailIndex *index, MailIndexRecord *rec)
{
MailIndexRecord *seekrec;
unsigned int seq;
i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
if (rec == index->last_lookup) {
/* same as last lookup sequence - too easy */
return index->last_lookup_seq;
}
if (index->header->first_hole_position == 0) {
/* easy, it's just at the expected index */
return INDEX_POSITION_INDEX(
INDEX_FILE_POSITION(index, rec)) + 1;
}
if (!mail_index_verify_hole_range(index))
return 0;
seekrec = (MailIndexRecord *) ((char *) index->mmap_base +
index->header->first_hole_position);
if (rec < seekrec) {
/* record before first hole */
return INDEX_POSITION_INDEX(
INDEX_FILE_POSITION(index, rec)) + 1;
}
/* we know the sequence after the first hole - skip to there and
start browsing the records until ours is found */
seq = INDEX_POSITION_INDEX(INDEX_FILE_POSITION(index, seekrec))+1;
seekrec += index->header->first_hole_records;
for (; seekrec < rec; seekrec++) {
if (seekrec->uid != 0)
seq++;
}
return seq;
}
static void index_mark_flag_changes(MailIndex *index, MailIndexRecord *rec,
MailFlags old_flags, MailFlags new_flags)
{
if ((old_flags & MAIL_SEEN) == 0 && (new_flags & MAIL_SEEN)) {
/* unseen -> seen */
index->header->seen_messages_count++;
} else if ((old_flags & MAIL_SEEN) && (new_flags & MAIL_SEEN) == 0) {
/* seen -> unseen */
if (index->header->seen_messages_count ==
index->header->messages_count) {
/* this is the first unseen message */
index->header->first_unseen_uid_lowwater = rec->uid;
} else if (rec->uid < index->header->first_unseen_uid_lowwater)
index->header->first_unseen_uid_lowwater = rec->uid;
if (index->header->seen_messages_count == 0)
INDEX_MARK_CORRUPTED(index);
else
index->header->seen_messages_count--;
} else if ((old_flags & MAIL_DELETED) == 0 &&
(new_flags & MAIL_DELETED)) {
/* undeleted -> deleted */
index->header->deleted_messages_count++;
if (index->header->deleted_messages_count == 1) {
/* this is the first deleted message */
index->header->first_deleted_uid_lowwater = rec->uid;
} else if (rec->uid < index->header->first_deleted_uid_lowwater)
index->header->first_deleted_uid_lowwater = rec->uid;
} else if ((old_flags & MAIL_DELETED) &&
(new_flags & MAIL_DELETED) == 0) {
/* deleted -> undeleted */
if (index->header->deleted_messages_count == 0)
INDEX_MARK_CORRUPTED(index);
else
index->header->deleted_messages_count--;
}
}
static void update_first_hole_records(MailIndex *index)
{
MailIndexRecord *rec, *end_rec;
/* see if first_hole_records can be grown */
rec = (MailIndexRecord *) ((char *) index->mmap_base +
index->header->first_hole_position) +
index->header->first_hole_records;
end_rec = (MailIndexRecord *) ((char *) index->mmap_base +
index->mmap_length);
while (rec < end_rec && rec->uid == 0) {
index->header->first_hole_records++;
rec++;
}
}
static int mail_index_truncate(MailIndex *index)
{
/* truncate index file */
if (ftruncate(index->fd, (off_t)index->header->first_hole_position) < 0)
return FALSE;
/* update header */
index->header->first_hole_position = 0;
index->header->first_hole_records = 0;
if (index->header->messages_count == 0) {
/* all mail was deleted, truncate data file */
if (!mail_index_data_reset(index->data))
return FALSE;
}
index->dirty_mmap = TRUE;
if (!mmap_update(index))
return FALSE;
return TRUE;
}
int mail_index_expunge(MailIndex *index, MailIndexRecord *rec,
unsigned int seq, int external_change)
{
MailIndexHeader *hdr;
uoff_t pos;
i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
i_assert(rec->uid != 0);
if (seq != 0) {
if (!mail_modifylog_add_expunge(index->modifylog, seq,
rec->uid, external_change))
return FALSE;
}
/* expunge() may be called while index is being rebuilt and when
there's no hash yet */
if (index->hash != NULL)
mail_hash_update(index->hash, rec->uid, 0);
/* setting UID to 0 is enough for deleting the mail from index */
rec->uid = 0;
/* update last_lookup_seq */
if (seq != 0) {
/* note that last_lookup can be left to point to
invalid record so that next() works properly */
if (seq == index->last_lookup_seq)
index->last_lookup = NULL;
else if (seq < index->last_lookup_seq)
index->last_lookup_seq--;
}
if (!mail_index_verify_hole_range(index))
return FALSE;
hdr = index->header;
/* update first hole */
pos = INDEX_FILE_POSITION(index, rec);
if (hdr->first_hole_position < sizeof(MailIndexRecord)) {
/* first deleted message in index */
hdr->first_hole_position = pos;
hdr->first_hole_records = 1;
} else if (hdr->first_hole_position - sizeof(MailIndexRecord) == pos) {
/* deleted the previous record before hole */
hdr->first_hole_position -= sizeof(MailIndexRecord);
hdr->first_hole_records++;
} else if (hdr->first_hole_position +
(hdr->first_hole_records * sizeof(MailIndexRecord)) == pos) {
/* deleted the next record after hole */
hdr->first_hole_records++;
update_first_hole_records(index);
} else {
/* second hole coming to index file, the index now needs to
be compressed to keep high performance */
index->set_flags |= MAIL_INDEX_FLAG_COMPRESS;
if (hdr->first_hole_position > pos) {
/* new hole before the old hole */
hdr->first_hole_position = pos;
hdr->first_hole_records = 1;
}
}
/* update message counts */
if (hdr->messages_count == 0) {
/* corrupted */
index_set_error(index, "Error in index file %s: "
"Header says there's no mail while expunging",
index->filepath);
INDEX_MARK_CORRUPTED(index);
return FALSE;
}
hdr->messages_count--;
index_mark_flag_changes(index, rec, rec->msg_flags, 0);
if ((hdr->first_hole_position - sizeof(MailIndexHeader)) /
sizeof(MailIndexRecord) == hdr->messages_count) {
/* the hole reaches end of file, truncate it */
(void)mail_index_truncate(index);
} else {
/* update deleted_space in data file */
(void)mail_index_data_add_deleted_space(index->data,
rec->data_size);
}
return TRUE;
}
int mail_index_update_flags(MailIndex *index, MailIndexRecord *rec,
unsigned int seq, MailFlags flags,
int external_change)
{
i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
i_assert(seq != 0);
if (flags == rec->msg_flags)
return TRUE; /* no changes */
index_mark_flag_changes(index, rec, rec->msg_flags, flags);
rec->msg_flags = flags;
return mail_modifylog_add_flags(index->modifylog, seq,
rec->uid, external_change);
}
int mail_index_append(MailIndex *index, MailIndexRecord **rec)
{
off_t pos;
i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
(*rec)->uid = index->header->next_uid++;
pos = lseek(index->fd, 0, SEEK_END);
if (pos < 0) {
index_set_error(index, "lseek() failed with file %s: %m",
index->filepath);
return FALSE;
}
if (write_full(index->fd, *rec, sizeof(MailIndexRecord)) < 0) {
index_set_error(index, "Error appending to file %s: %m",
index->filepath);
return FALSE;
}
index->header->messages_count++;
index_mark_flag_changes(index, *rec, 0, (*rec)->msg_flags);
if (index->hash != NULL)
mail_hash_update(index->hash, (*rec)->uid, (uoff_t)pos);
index->dirty_mmap = TRUE;
if (!mmap_update(index))
return FALSE;
*rec = (MailIndexRecord *) ((char *) index->mmap_base + pos);
return TRUE;
}
const char *mail_index_get_last_error(MailIndex *index)
{
return index->error;
}
int mail_index_is_inconsistency_error(MailIndex *index)
{
return index->inconsistent;
}