mail-index-open.c revision d3a288c47d1e99627ef3c525c654dc5430443b97
5e0ce63bb65db34d7f48b34bbb5545fa791781c4Timo Sirainen/* Copyright (C) 2002 Timo Sirainen */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "lib.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "ioloop.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "file-lock.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "file-set-size.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "hostpid.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "mmap-util.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "unlink-lockfiles.h"
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#include "write-full.h"
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#include "mail-index.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "mail-index-data.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "mail-index-util.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "mail-tree.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "mail-lockdir.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "mail-modifylog.h"
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen#include "mail-custom-flags.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include <stdio.h>
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen#include <stdlib.h>
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include <unistd.h>
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include <fcntl.h>
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic const char *index_file_prefixes[] =
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen { "data", "tree", "log", "log.2", NULL };
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainenstatic int delete_index(const char *path)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen char tmp[1024];
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen int i;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* main index */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (unlink(path) < 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen for (i = 0; index_file_prefixes[i] != NULL; i++) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i_snprintf(tmp, sizeof(tmp), "%s.%s",
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen path, index_file_prefixes[i]);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (unlink(tmp) < 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen i++;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return TRUE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int read_and_verify_header(int fd, MailIndexHeader *hdr,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen int check_version)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen /* read the header */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (lseek(fd, 0, SEEK_SET) != 0)
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen return FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (read(fd, hdr, sizeof(MailIndexHeader)) != sizeof(MailIndexHeader))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen /* check the compatibility */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return hdr->compat_data[1] == MAIL_INDEX_COMPAT_FLAGS &&
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen hdr->compat_data[2] == sizeof(unsigned int) &&
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen hdr->compat_data[3] == sizeof(time_t) &&
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen hdr->compat_data[4] == sizeof(uoff_t) &&
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen hdr->compat_data[5] == MEM_ALIGN_SIZE &&
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen (!check_version || hdr->compat_data[0] == MAIL_INDEX_VERSION);
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen}
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen/* Returns TRUE if we're compatible with given index file. May delete the
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen file if it's from older version. */
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainenstatic int mail_check_compatible_index(MailIndex *index, const char *path)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen MailIndexHeader hdr;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen int fd, compatible;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen fd = open(path, O_RDONLY);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (fd == -1) {
af81f402ddc897c74c1e85abd02879612ce44882Timo Sirainen if (errno != ENOENT)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen index_file_set_syscall_error(index, path, "open()");
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen compatible = read_and_verify_header(fd, &hdr, FALSE);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (hdr.compat_data[0] != MAIL_INDEX_VERSION) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen /* version mismatch */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen compatible = FALSE;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (hdr.compat_data[0] < MAIL_INDEX_VERSION) {
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen /* of older version, we don't need it anymore */
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen (void)delete_index(path);
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen }
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen (void)close(fd);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return compatible;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen/* Returns a file name of compatible index */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic const char *mail_find_index(MailIndex *index)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const char *name;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen char path[1024];
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen hostpid_init();
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* first try .imap.index-<hostname> */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen name = t_strconcat(INDEX_FILE_PREFIX "-", my_hostname, NULL);
b3142c8e513bc78da821fa70f479016148fa95e5Timo Sirainen i_snprintf(path, sizeof(path), "%s/%s", index->dir, name);
af81f402ddc897c74c1e85abd02879612ce44882Timo Sirainen if (mail_check_compatible_index(index, path))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return name;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
b3142c8e513bc78da821fa70f479016148fa95e5Timo Sirainen /* then try the generic .imap.index */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen name = INDEX_FILE_PREFIX;
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen i_snprintf(path, sizeof(path), "%s/%s", index->dir, name);
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen if (mail_check_compatible_index(index, path))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return name;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen return NULL;
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen}
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainenstatic int mail_index_open_init(MailIndex *index, int update_recent)
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen{
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen MailIndexHeader *hdr;
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen hdr = index->header;
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen /* update \Recent message counters */
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen if (update_recent && hdr->last_nonrecent_uid != hdr->next_uid-1) {
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen /* keep last_recent_uid to next_uid-1 */
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen if (index->lock_type == MAIL_LOCK_SHARED) {
67c24901ac5e1521e38a91efc452faeb3e2135a1Timo Sirainen if (!index->set_lock(index, MAIL_LOCK_UNLOCK))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE))
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen return FALSE;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen index->first_recent_uid = index->header->last_nonrecent_uid+1;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen index->header->last_nonrecent_uid = index->header->next_uid-1;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen } else {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen index->first_recent_uid = hdr->last_nonrecent_uid+1;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (hdr->next_uid >= INT_MAX-1024) {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen /* UID values are getting too high, rebuild index */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen index->set_flags |= MAIL_INDEX_FLAG_REBUILD;
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (index->lock_type == MAIL_LOCK_EXCLUSIVE) {
b3142c8e513bc78da821fa70f479016148fa95e5Timo Sirainen /* finally reset the modify log marks, fsck or syncing might
b3142c8e513bc78da821fa70f479016148fa95e5Timo Sirainen have deleted some messages, and since we're only just
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen opening the index, there's no need to remember them */
b3142c8e513bc78da821fa70f479016148fa95e5Timo Sirainen if (!mail_modifylog_mark_synced(index->modifylog))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
b3142c8e513bc78da821fa70f479016148fa95e5Timo Sirainen return TRUE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int index_open_and_fix(MailIndex *index, int update_recent, int fast)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen int rebuild;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (!mail_index_mmap_update(index))
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return FALSE;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen rebuild = FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* open/create the index files */
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if (!mail_index_data_open(index)) {
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen if ((index->set_flags & MAIL_INDEX_FLAG_REBUILD) == 0)
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen return FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen /* data file is corrupted, need to rebuild index */
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen rebuild = TRUE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen index->set_flags = 0;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (!mail_index_data_create(index))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* custom flags file needs to be open before
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen rebuilding index */
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (!mail_custom_flags_open_or_create(index))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return FALSE;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (rebuild || (index->header->flags & MAIL_INDEX_FLAG_REBUILD)) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* index is corrupted, rebuild */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (!index->rebuild(index))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* no inconsistency problems while still opening
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen the index */
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen index->inconsistent = FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (!mail_tree_open_or_create(index))
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen return FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (!mail_modifylog_open_or_create(index))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return FALSE;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (index->header->flags & MAIL_INDEX_FLAG_FSCK) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen /* index needs fscking */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if (!index->fsck(index))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return FALSE;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if (!fast && (index->header->flags & MAIL_INDEX_FLAG_COMPRESS)) {
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen /* remove deleted blocks from index file */
03f5c621d06d6b6d77a145196c9633a7aa64dc78Timo Sirainen if (!mail_index_compress(index))
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return FALSE;
}
if (index->header->flags & MAIL_INDEX_FLAG_REBUILD_TREE) {
if (!mail_tree_rebuild(index->tree))
return FALSE;
}
/* sync ourself - before updating cache and compression which
may happen because of this. */
if (!index->sync_and_lock(index, MAIL_LOCK_SHARED, NULL))
return FALSE;
/* we never want to keep shared lock if syncing happens to set it.
either exclusive or nothing (NOTE: drop it directly, not through
index->set_lock() so mbox lock won't be affected). */
if (index->lock_type == MAIL_LOCK_SHARED) {
if (!mail_index_set_lock(index, MAIL_LOCK_UNLOCK))
return FALSE;
}
if (!fast && (index->header->flags & MAIL_INDEX_FLAG_CACHE_FIELDS)) {
/* need to update cached fields */
if (!mail_index_update_cache(index))
return FALSE;
}
if (!fast && (index->header->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))
return FALSE;
}
if (!mail_index_open_init(index, update_recent))
return FALSE;
return TRUE;
}
static int mail_index_open_file(MailIndex *index, const char *path,
int update_recent, int fast)
{
MailIndexHeader hdr;
int fd, failed;
/* the index file should already be checked that it exists and
we're compatible with it. */
fd = open(path, O_RDWR);
if (fd == -1)
return index_file_set_syscall_error(index, path, "open()");
/* if index is being created, we'll wait here until it's finished */
if (file_wait_lock(fd, F_RDLCK, DEFAULT_LOCK_TIMEOUT) <= 0) {
index_file_set_syscall_error(index, path, "file_wait_lock()");
(void)close(fd);
return FALSE;
}
/* check the compatibility anyway just to be sure */
if (!read_and_verify_header(fd, &hdr, TRUE)) {
index_set_error(index, "Non-compatible index file %s", path);
(void)close(fd);
return FALSE;
}
if (file_wait_lock(fd, F_UNLCK, 0) <= 0) {
index_file_set_syscall_error(index, path, "file_wait_lock()");
(void)close(fd);
return FALSE;
}
index->fd = fd;
index->filepath = i_strdup(path);
index->indexid = hdr.indexid;
failed = !index_open_and_fix(index, update_recent, fast);
if (!index->set_lock(index, MAIL_LOCK_UNLOCK))
failed = TRUE;
if (failed) {
mail_index_close(index);
return FALSE;
}
return TRUE;
}
static int mail_index_init_new_file(MailIndex *index, MailIndexHeader *hdr,
int fd, const char *path,
const char **index_path)
{
off_t fsize;
*index_path = NULL;
if (write_full(fd, hdr, sizeof(MailIndexHeader)) < 0) {
index_file_set_syscall_error(index, path, "write_full()");
return FALSE;
}
fsize = sizeof(MailIndexHeader) +
INDEX_MIN_RECORDS_COUNT * sizeof(MailIndexRecord);
if (file_set_size(fd, (off_t)fsize) < 0) {
index_file_set_syscall_error(index, path, "file_set_size()");
return FALSE;
}
if (file_wait_lock(fd, F_WRLCK, DEFAULT_LOCK_TIMEOUT) <= 0) {
index_file_set_syscall_error(index, path, "file_wait_lock()");
return FALSE;
}
/* move the temp index into the real one. we also need to figure
out what to call ourself on the way. */
*index_path = t_strconcat(index->dir, "/"INDEX_FILE_PREFIX, NULL);
if (link(path, *index_path) == 0) {
if (unlink(path) < 0) {
/* doesn't really matter, log anyway */
index_file_set_syscall_error(index, path, "unlink()");
}
} else {
if (errno != EEXIST) {
/* fatal error */
index_set_error(index, "link(%s, %s) failed: %m",
path, *index_path);
return FALSE;
}
if (getenv("OVERWRITE_INCOMPATIBLE_INDEX") != NULL) {
/* 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();
*index_path = t_strconcat(*index_path, "-",
my_hostname, NULL);
}
if (rename(path, *index_path) < 0) {
index_set_error(index, "rename(%s, %s) failed: %m",
path, *index_path);
return FALSE;
}
}
return TRUE;
}
static int mail_index_create(MailIndex *index, int *dir_unlocked,
int update_recent)
{
MailIndexHeader hdr;
const char *path, *index_path;
int fd, nodiskspace;
*dir_unlocked = FALSE;
index_path = NULL;
mail_index_init_header(index, &hdr);
if (index->nodiskspace) {
/* don't even bother trying to create it */
fd = -1;
} else {
/* first create the index into temporary file. */
fd = mail_index_create_temp_file(index, &path);
if (fd != -1) {
if (!mail_index_init_new_file(index, &hdr, fd,
path, &index_path)) {
int old_errno = errno;
(void)close(fd);
(void)unlink(path);
fd = -1;
errno = old_errno;
}
}
if (fd == -1 && errno != ENOSPC) {
/* fatal failure */
return FALSE;
}
}
if (fd == -1) {
/* no space for index files, keep it in memory */
index->mmap_full_length = INDEX_FILE_MIN_SIZE;
index->mmap_base = mmap_anon(index->mmap_full_length);
memcpy(index->mmap_base, &hdr, sizeof(hdr));
index->header = index->mmap_base;
index->mmap_used_length = index->header->used_file_size;
index->anon_mmap = TRUE;
index->filepath = i_strdup("(in-memory index)");
} else {
index->filepath = i_strdup(index_path);
}
index->fd = fd;
index->indexid = hdr.indexid;
/* the fd is actually already locked, now we're just making it
clear to the indexing code. */
if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE)) {
mail_index_close(index);
return FALSE;
}
/* it's not good to keep the directory locked too long. our index file
is locked which is enough. */
if (!*dir_unlocked && mail_index_lock_dir(index, MAIL_LOCK_UNLOCK))
*dir_unlocked = TRUE;
do {
if (!mail_custom_flags_open_or_create(index))
break;
if (!mail_index_data_create(index))
break;
nodiskspace = index->nodiskspace;
if (!index->rebuild(index)) {
if (!index->anon_mmap && index->nodiskspace) {
/* we're out of disk space, keep it in
memory this time */
mail_index_close(index);
index->nodiskspace = TRUE;
return mail_index_create(index, dir_unlocked,
update_recent);
}
break;
}
/* rebuild() resets the nodiskspace variable */
index->nodiskspace = nodiskspace;
if (!mail_tree_create(index))
break;
if (!mail_modifylog_create(index))
break;
index->inconsistent = FALSE;
if (!mail_index_open_init(index, update_recent))
break;
if (!index->set_lock(index, MAIL_LOCK_UNLOCK))
break;
return TRUE;
} while (0);
(void)index->set_lock(index, MAIL_LOCK_UNLOCK);
mail_index_close(index);
return FALSE;
}
void mail_index_init_header(MailIndex *index, 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 requiring rebuild - rebuild() removes this flag
when it succeeds */
hdr->flags = MAIL_INDEX_FLAG_REBUILD;
/* set the fields we always want to cache */
hdr->cache_fields |= index->default_cache_fields;
hdr->used_file_size = sizeof(MailIndexHeader);
hdr->uid_validity = ioloop_time;
hdr->next_uid = 1;
}
static void mail_index_cleanup_temp_files(const char *dir)
{
unlink_lockfiles(dir, t_strconcat("temp.", my_hostname, NULL),
"temp.", time(NULL) - TEMP_FILE_TIMEOUT);
}
int mail_index_open(MailIndex *index, int update_recent, int fast)
{
const char *name, *path;
i_assert(!index->opened);
/* this isn't initialized anywhere else */
index->fd = -1;
mail_index_cleanup_temp_files(index->dir);
name = mail_find_index(index);
if (name == NULL)
return FALSE;
path = t_strconcat(index->dir, "/", name, NULL);
if (!mail_index_open_file(index, path, update_recent, fast))
return FALSE;
index->opened = TRUE;
return TRUE;
}
int mail_index_open_or_create(MailIndex *index, int update_recent, int fast)
{
int failed, dir_unlocked;
i_assert(!index->opened);
mail_index_cleanup_temp_files(index->dir);
if (mail_index_open(index, update_recent, fast))
return TRUE;
/* index wasn't found or it was broken. lock the directory 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;
if (mail_index_open(index, update_recent, fast)) {
dir_unlocked = FALSE;
failed = FALSE;
} else {
failed = !mail_index_create(index, &dir_unlocked,
update_recent);
}
if (!dir_unlocked && !mail_index_lock_dir(index, MAIL_LOCK_UNLOCK))
return FALSE;
if (failed)
return FALSE;
index->opened = TRUE;
return TRUE;
}