mail-index.c revision 003079baf8056c81c25162a176a89f9169cc374f
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "array.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "buffer.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "hash.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mmap-util.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "nfs-workarounds.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include "read-full.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include "write-full.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "mail-index-private.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include "mail-index-sync-private.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include "mail-transaction-log.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include "mail-cache.h"
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include <stdio.h>
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include <stddef.h>
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include <time.h>
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#include <sys/stat.h>
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenunsigned int mail_index_module_id = 0;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenstatic int mail_index_try_open_only(struct mail_index *index);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void mail_index_create_in_memory(struct mail_index *index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_index_header *hdr);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct mail_index *mail_index_alloc(const char *dir, const char *prefix)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_index *index;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen index = i_new(struct mail_index, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen index->dir = i_strdup(dir);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen index->prefix = i_strdup(prefix);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen index->fd = -1;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen index->extension_pool = pool_alloconly_create("extension", 512);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ARRAY_CREATE(&index->extensions, index->extension_pool,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_index_registered_ext, 5);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen ARRAY_CREATE(&index->sync_lost_handlers, default_pool,
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen mail_index_sync_lost_handler_t *, 4);
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen ARRAY_CREATE(&index->mail_index_module_contexts, default_pool,
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen void *, I_MIN(5, mail_index_module_id));
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen index->mode = 0600;
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen index->gid = (gid_t)-1;
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen index->keywords_ext_id =
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen mail_index_ext_register(index, "keywords", 128, 2, 1);
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen index->keywords_pool = pool_alloconly_create("keywords", 512);
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen ARRAY_CREATE(&index->keywords, default_pool, const char *, 16);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen index->keywords_hash =
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen hash_create(default_pool, index->keywords_pool, 0,
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen strcase_hash, (hash_cmp_callback_t *)strcasecmp);
ea546eaab672d441e180b7619d4750be813c08d8Timo Sirainen return index;
ea546eaab672d441e180b7619d4750be813c08d8Timo Sirainen}
ea546eaab672d441e180b7619d4750be813c08d8Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_index_free(struct mail_index **_index)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_index *index = *_index;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *_index = NULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_close(index);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen hash_destroy(index->keywords_hash);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen pool_unref(index->extension_pool);
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen pool_unref(index->keywords_pool);
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen array_free(&index->sync_lost_handlers);
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen array_free(&index->keywords);
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen i_free(index->error);
5c1a8aee989af87bddefd71e2aa83aa2bd695155Timo Sirainen i_free(index->dir);
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen i_free(index->prefix);
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen i_free(index);
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen}
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainenvoid mail_index_set_permissions(struct mail_index *index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mode_t mode, gid_t gid)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
ec1e30ecc38f0deddaf655413cf02d5972ddbc70Timo Sirainen index->mode = mode & 0666;
ec1e30ecc38f0deddaf655413cf02d5972ddbc70Timo Sirainen index->gid = gid;
ec1e30ecc38f0deddaf655413cf02d5972ddbc70Timo Sirainen}
ec1e30ecc38f0deddaf655413cf02d5972ddbc70Timo Sirainen
ec1e30ecc38f0deddaf655413cf02d5972ddbc70Timo Sirainenuint32_t mail_index_ext_register(struct mail_index *index, const char *name,
ec1e30ecc38f0deddaf655413cf02d5972ddbc70Timo Sirainen uint32_t default_hdr_size,
ec1e30ecc38f0deddaf655413cf02d5972ddbc70Timo Sirainen uint16_t default_record_size,
ec1e30ecc38f0deddaf655413cf02d5972ddbc70Timo Sirainen uint16_t default_record_align)
ec1e30ecc38f0deddaf655413cf02d5972ddbc70Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_index_registered_ext *extensions;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_index_registered_ext rext;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int i, ext_count;
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen extensions = array_get(&index->extensions, &ext_count);
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* see if it's already there */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for (i = 0; i < ext_count; i++) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (strcmp(extensions[i].name, name) == 0)
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen return i;
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen memset(&rext, 0, sizeof(rext));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rext.name = p_strdup(index->extension_pool, name);
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen rext.index_idx = ext_count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rext.hdr_size = default_hdr_size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rext.record_size = default_record_size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rext.record_align = default_record_align;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen array_append(&index->extensions, &rext, 1);
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen return ext_count;
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen}
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainenvoid mail_index_register_expunge_handler(struct mail_index *index,
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen uint32_t ext_id,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_expunge_handler_t *cb,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen void *context, bool call_always)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_index_registered_ext *rext;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rext = array_idx_modifiable(&index->extensions, ext_id);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(rext->expunge_handler == NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen rext->expunge_handler = cb;
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen rext->expunge_context = context;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rext->expunge_handler_call_always = call_always;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_index_unregister_expunge_handler(struct mail_index *index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uint32_t ext_id)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_index_registered_ext *rext;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rext = array_idx_modifiable(&index->extensions, ext_id);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(rext->expunge_handler != NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rext->expunge_handler = NULL;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid mail_index_register_sync_handler(struct mail_index *index, uint32_t ext_id,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_sync_handler_t *cb,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen enum mail_index_sync_handler_type type)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_index_registered_ext *rext;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen rext = array_idx_modifiable(&index->extensions, ext_id);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen i_assert(rext->sync_handler.callback == NULL);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen rext->sync_handler.callback = cb;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen rext->sync_handler.type = type;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen}
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainenvoid mail_index_unregister_sync_handler(struct mail_index *index,
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen uint32_t ext_id)
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen{
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen struct mail_index_registered_ext *rext;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen rext = array_idx_modifiable(&index->extensions, ext_id);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen i_assert(rext->sync_handler.callback != NULL);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen rext->sync_handler.callback = NULL;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen rext->sync_handler.type = 0;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen}
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainenvoid mail_index_register_sync_lost_handler(struct mail_index *index,
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen mail_index_sync_lost_handler_t *cb)
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen{
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen array_append(&index->sync_lost_handlers, &cb, 1);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen}
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainenvoid mail_index_unregister_sync_lost_handler(struct mail_index *index,
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen mail_index_sync_lost_handler_t *cb)
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen{
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen mail_index_sync_lost_handler_t *const *handlers;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen unsigned int i, count;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen handlers = array_get(&index->sync_lost_handlers, &count);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen for (i = 0; i < count; i++) {
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen if (handlers[i] == cb) {
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen array_delete(&index->sync_lost_handlers, i, 1);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen break;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen }
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen }
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen}
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenstatic void mail_index_map_init_extbufs(struct mail_index_map *map,
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen unsigned int initial_count)
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen{
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#define EXTENSION_NAME_APPROX_LEN 20
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#define EXT_GLOBAL_ALLOC_SIZE \
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen ((sizeof(map->extensions) + BUFFER_APPROX_SIZE) * 2)
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#define EXT_PER_ALLOC_SIZE \
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (EXTENSION_NAME_APPROX_LEN + \
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen sizeof(struct mail_index_ext) + sizeof(uint32_t))
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen size_t size;
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen
5a58037ad75b88356d82240fab2bc604de03107eTimo Sirainen if (map->extension_pool == NULL) {
5a58037ad75b88356d82240fab2bc604de03107eTimo Sirainen size = EXT_GLOBAL_ALLOC_SIZE +
5a58037ad75b88356d82240fab2bc604de03107eTimo Sirainen initial_count * EXT_PER_ALLOC_SIZE;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen map->extension_pool =
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen pool_alloconly_create("extensions",
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen nearest_power(size));
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen } else {
5a58037ad75b88356d82240fab2bc604de03107eTimo Sirainen p_clear(map->extension_pool);
5a58037ad75b88356d82240fab2bc604de03107eTimo Sirainen
5a58037ad75b88356d82240fab2bc604de03107eTimo Sirainen /* try to use the existing pool's size for initial_count so
5a58037ad75b88356d82240fab2bc604de03107eTimo Sirainen we don't grow it unneededly */
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen size = p_get_max_easy_alloc_size(map->extension_pool);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (size > EXT_GLOBAL_ALLOC_SIZE + EXT_PER_ALLOC_SIZE) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen initial_count = (size - EXT_GLOBAL_ALLOC_SIZE) /
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen EXT_PER_ALLOC_SIZE;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen }
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ARRAY_CREATE(&map->extensions, map->extension_pool,
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen struct mail_index_ext, initial_count);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ARRAY_CREATE(&map->ext_id_map, map->extension_pool,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uint32_t, initial_count);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainenuint32_t mail_index_map_lookup_ext(struct mail_index_map *map, const char *name)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_index_ext *extensions;
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen unsigned int i, size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen if (!array_is_created(&map->extensions))
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return (uint32_t)-1;
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen extensions = array_get(&map->extensions, &size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for (i = 0; i < size; i++) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (strcmp(extensions[i].name, name) == 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return i;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return (uint32_t)-1;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen}
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenuint32_t
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_index_map_register_ext(struct mail_index *index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_index_map *map, const char *name,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uint32_t hdr_offset, uint32_t hdr_size,
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen uint32_t record_offset, uint32_t record_size,
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen uint32_t record_align, uint32_t reset_id)
5529671faac3c5672a948be93091056736c7afffTimo Sirainen{
5529671faac3c5672a948be93091056736c7afffTimo Sirainen struct mail_index_ext *ext;
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen uint32_t idx, empty_idx = (uint32_t)-1;
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen if (!array_is_created(&map->extensions)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_map_init_extbufs(map, 5);
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen idx = 0;
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen } else {
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen idx = array_count(&map->extensions);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen i_assert(mail_index_map_lookup_ext(map, name) == (uint32_t)-1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen ext = array_append_space(&map->extensions);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen ext->name = p_strdup(map->extension_pool, name);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen ext->hdr_offset = hdr_offset;
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen ext->hdr_size = hdr_size;
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen ext->record_offset = record_offset;
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen ext->record_size = record_size;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen ext->record_align = record_align;
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen ext->reset_id = reset_id;
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen ext->index_idx = mail_index_ext_register(index, name, hdr_size,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen record_size, record_align);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* Update index ext_id -> map ext_id mapping. Fill non-used
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen ext_ids with (uint32_t)-1 */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen while (array_count(&map->ext_id_map) < ext->index_idx)
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen array_append(&map->ext_id_map, &empty_idx, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen array_idx_set(&map->ext_id_map, ext->index_idx, &idx);
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen return idx;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainenstatic bool size_check(size_t *size_left, size_t size)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (size > *size_left)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *size_left -= size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainenstatic size_t get_align(size_t name_len)
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen size_t size = sizeof(struct mail_index_ext_header) + name_len;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen return MAIL_INDEX_HEADER_SIZE_ALIGN(size) - size;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen}
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainenstatic int mail_index_parse_extensions(struct mail_index *index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_index_map *map)
62950eeff28f00989a17b20eeade3af7e200c6bcTimo Sirainen{
5a58037ad75b88356d82240fab2bc604de03107eTimo Sirainen const struct mail_index_ext_header *ext_hdr;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int i, old_count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *name;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uint32_t ext_id, offset, name_offset;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen size_t size_left;
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen /* extension headers always start from 64bit offsets, so if base header
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen doesn't happen to be 64bit aligned we'll skip some bytes */
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen offset = MAIL_INDEX_HEADER_SIZE_ALIGN(map->hdr.base_header_size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (offset >= map->hdr.header_size && map->extension_pool == NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* nothing to do, skip allocatations and all */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return 1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
5a58037ad75b88356d82240fab2bc604de03107eTimo Sirainen old_count = array_count(&index->extensions);
62950eeff28f00989a17b20eeade3af7e200c6bcTimo Sirainen mail_index_map_init_extbufs(map, old_count + 5);
62950eeff28f00989a17b20eeade3af7e200c6bcTimo Sirainen
62950eeff28f00989a17b20eeade3af7e200c6bcTimo Sirainen ext_id = (uint32_t)-1;
62950eeff28f00989a17b20eeade3af7e200c6bcTimo Sirainen for (i = 0; i < old_count; i++)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen array_append(&map->ext_id_map, &ext_id, 1);
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen while (offset < map->hdr.header_size) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ext_hdr = CONST_PTR_OFFSET(map->hdr_base, offset);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen /* Extension header contains:
5a58037ad75b88356d82240fab2bc604de03107eTimo Sirainen - struct mail_index_ext_header
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - name (not 0-terminated)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - 64bit alignment padding
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen - extension header contents
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen - 64bit alignment padding
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen size_left = map->hdr.header_size - offset;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!size_check(&size_left, sizeof(*ext_hdr)) ||
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen !size_check(&size_left, ext_hdr->name_size) ||
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen !size_check(&size_left, get_align(ext_hdr->name_size)) ||
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen !size_check(&size_left, ext_hdr->hdr_size)) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen "Header extension goes outside header",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen index->filepath);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen offset += sizeof(*ext_hdr);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen name_offset = offset;
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen offset += ext_hdr->name_size + get_align(ext_hdr->name_size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_push();
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen name = t_strndup(CONST_PTR_OFFSET(map->hdr_base, name_offset),
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen ext_hdr->name_size);
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen if (mail_index_map_lookup_ext(map, name) != (uint32_t)-1) {
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen "Duplicate header extension %s",
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen index->filepath, name);
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen t_pop();
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((ext_hdr->record_offset % ext_hdr->record_align) != 0 ||
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen (map->hdr.record_size % ext_hdr->record_align) != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Record field %s alignmentation %u not used",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen index->filepath, name, ext_hdr->record_align);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_pop();
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_index_map_register_ext(index, map, name,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen offset, ext_hdr->hdr_size,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ext_hdr->record_offset,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ext_hdr->record_size,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ext_hdr->record_align,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ext_hdr->reset_id);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen t_pop();
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen offset += MAIL_INDEX_HEADER_SIZE_ALIGN(ext_hdr->hdr_size);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen }
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen return 1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenbool mail_index_keyword_lookup(struct mail_index *index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *keyword, bool autocreate,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int *idx_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen char *keyword_dup;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen void *value;
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* keywords_hash keeps a name => index mapping of keywords.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen Keywords are never removed from it, so the index values are valid
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for the lifetime of the mail_index. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (hash_lookup_full(index->keywords_hash, keyword, NULL, &value)) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *idx_r = POINTER_CAST_TO(value, unsigned int);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (!autocreate) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *idx_r = (unsigned int)-1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen keyword = keyword_dup = p_strdup(index->keywords_pool, keyword);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *idx_r = array_count(&index->keywords);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen hash_insert(index->keywords_hash, keyword_dup, POINTER_CAST(*idx_r));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen array_append(&index->keywords, &keyword, 1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainenint mail_index_map_parse_keywords(struct mail_index *index,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_index_map *map)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_index_ext *ext;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_index_keyword_header *kw_hdr;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_index_keyword_header_rec *kw_rec;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *name;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int i, name_area_end_offset, old_count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uint32_t ext_id;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen ext_id = mail_index_map_lookup_ext(map, "keywords");
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen if (ext_id == (uint32_t)-1) {
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen if (array_is_created(&map->keyword_idx_map))
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen array_clear(&map->keyword_idx_map);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen return 0;
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen }
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen ext = array_idx(&map->extensions, ext_id);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen /* Extension header contains:
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen - struct mail_index_keyword_header
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen - struct mail_index_keyword_header_rec * keywords_count
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen - const char names[] * keywords_count
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen */
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen i_assert(ext->hdr_offset < map->hdr.header_size);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen kw_hdr = CONST_PTR_OFFSET(map->hdr_base, ext->hdr_offset);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen kw_rec = (const void *)(kw_hdr + 1);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen name = (const char *)(kw_rec + kw_hdr->keywords_count);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen old_count = !array_is_created(&map->keyword_idx_map) ? 0 :
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen array_count(&map->keyword_idx_map);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen /* Keywords can only be added into same mapping. Removing requires a
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen new mapping (recreating the index file) */
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen if (kw_hdr->keywords_count == old_count) {
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen /* nothing changed */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen return 0;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen }
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* make sure the header is valid */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen if (kw_hdr->keywords_count < old_count) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen "Keywords removed unexpectedly",
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen index->filepath);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen return -1;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen }
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen if ((size_t)(name - (const char *)kw_hdr) > ext->hdr_size) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen "keywords_count larger than header size",
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen index->filepath);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen return -1;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen }
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen name_area_end_offset = (const char *)kw_hdr + ext->hdr_size - name;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen for (i = 0; i < kw_hdr->keywords_count; i++) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen if (kw_rec[i].name_offset > name_area_end_offset) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen "name_offset points outside allocated header",
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen index->filepath);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen return -1;
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen }
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen }
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen if (name[name_area_end_offset-1] != '\0') {
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen "Keyword header doesn't end with NUL",
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen index->filepath);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen return -1;
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen }
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen /* create file -> index mapping */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen if (!array_is_created(&map->keyword_idx_map)) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen ARRAY_CREATE(&map->keyword_idx_map, default_pool,
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen unsigned int, kw_hdr->keywords_count);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen }
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#ifdef DEBUG
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* Check that existing headers are still the same. It's behind DEBUG
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen since it's pretty useless waste of CPU normally. */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen for (i = 0; i < array_count(&map->keyword_idx_map); i++) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen const char *keyword = name + kw_rec[i].name_offset;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen const unsigned int *old_idx;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen unsigned int idx;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen old_idx = array_idx(&map->keyword_idx_map, i);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen if (!mail_index_keyword_lookup(index, keyword, FALSE, &idx) ||
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen idx != *old_idx) {
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen "Keywords changed unexpectedly",
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen index->filepath);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen return -1;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen }
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen }
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#endif
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* Register the newly seen keywords */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen i = array_count(&map->keyword_idx_map);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen for (; i < kw_hdr->keywords_count; i++) {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen const char *keyword = name + kw_rec[i].name_offset;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen unsigned int idx;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen (void)mail_index_keyword_lookup(index, keyword, TRUE, &idx);
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen array_append(&map->keyword_idx_map, &idx, 1);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen }
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen return 0;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen}
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenconst ARRAY_TYPE(keywords) *mail_index_get_keywords(struct mail_index *index)
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen{
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* Make sure all the keywords are in index->keywords. It's quick to do
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen if nothing has changed. */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen (void)mail_index_map_parse_keywords(index, index->map);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen return &index->keywords;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen}
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenstatic int mail_index_check_header(struct mail_index *index,
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen struct mail_index_map *map)
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen{
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen const struct mail_index_header *hdr = &map->hdr;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen enum mail_index_header_compat_flags compat_flags = 0;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen#ifndef WORDS_BIGENDIAN
compat_flags |= MAIL_INDEX_COMPAT_LITTLE_ENDIAN;
#endif
if (hdr->major_version != MAIL_INDEX_MAJOR_VERSION) {
/* major version change - handle silently(?) */
return -1;
}
if (hdr->compat_flags != compat_flags) {
/* architecture change - handle silently(?) */
return -1;
}
if ((map->hdr.flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0) {
/* we've already complained about it */
return -1;
}
/* following some extra checks that only take a bit of CPU */
if (hdr->uid_validity == 0 && hdr->next_uid != 1) {
mail_index_set_error(index, "Corrupted index file %s: "
"uid_validity = 0, next_uid = %u",
index->filepath, hdr->next_uid);
return -1;
}
if (hdr->record_size < sizeof(struct mail_index_record)) {
mail_index_set_error(index, "Corrupted index file %s: "
"record_size too small: %u < %"PRIuSIZE_T,
index->filepath, hdr->record_size,
sizeof(struct mail_index_record));
return -1;
}
if ((hdr->flags & MAIL_INDEX_HDR_FLAG_FSCK) != 0)
return 0;
if (hdr->next_uid == 0)
return 0;
if (hdr->recent_messages_count > hdr->messages_count ||
hdr->seen_messages_count > hdr->messages_count ||
hdr->deleted_messages_count > hdr->messages_count)
return 0;
if (hdr->first_recent_uid_lowwater > hdr->next_uid ||
hdr->first_unseen_uid_lowwater > hdr->next_uid ||
hdr->first_deleted_uid_lowwater > hdr->next_uid)
return 0;
return mail_index_parse_extensions(index, map);
}
static void mail_index_map_clear(struct mail_index *index,
struct mail_index_map *map)
{
if (map->buffer != NULL) {
i_assert(map->mmap_base == NULL);
buffer_free(map->buffer);
map->buffer = NULL;
} else if (map->mmap_base != NULL) {
i_assert(map->buffer == NULL);
if (munmap(map->mmap_base, map->mmap_size) < 0)
mail_index_set_syscall_error(index, "munmap()");
map->mmap_base = NULL;
}
if (map->refcount > 0) {
memset(&map->hdr, 0, sizeof(map->hdr));
map->mmap_size = 0;
map->mmap_used_size = 0;
map->records = NULL;
map->records_count = 0;
}
}
void mail_index_unmap(struct mail_index *index, struct mail_index_map **_map)
{
struct mail_index_map *map = *_map;
*_map = NULL;
if (--map->refcount > 0)
return;
i_assert(map->refcount == 0);
mail_index_map_clear(index, map);
if (map->extension_pool != NULL)
pool_unref(map->extension_pool);
if (array_is_created(&map->keyword_idx_map))
array_free(&map->keyword_idx_map);
buffer_free(map->hdr_copy_buf);
i_free(map);
}
static void mail_index_map_copy_hdr(struct mail_index_map *map,
const struct mail_index_header *hdr)
{
if (hdr->base_header_size < sizeof(map->hdr)) {
/* header smaller than ours, make a copy so our newer headers
won't have garbage in them */
memset(&map->hdr, 0, sizeof(map->hdr));
memcpy(&map->hdr, hdr, hdr->base_header_size);
} else {
map->hdr = *hdr;
}
}
static int mail_index_mmap(struct mail_index *index, struct mail_index_map *map)
{
const struct mail_index_header *hdr;
unsigned int records_count;
i_assert(!map->write_to_disk);
if (map->buffer != NULL) {
/* we had temporarily used a buffer, eg. for updating index */
buffer_free(map->buffer);
map->buffer = NULL;
}
map->mmap_base = index->readonly ?
mmap_ro_file(index->fd, &map->mmap_size) :
mmap_rw_file(index->fd, &map->mmap_size);
if (map->mmap_base == MAP_FAILED) {
map->mmap_base = NULL;
mail_index_set_syscall_error(index, "mmap()");
return -1;
}
hdr = map->mmap_base;
if (map->mmap_size >
offsetof(struct mail_index_header, major_version) &&
hdr->major_version != MAIL_INDEX_MAJOR_VERSION) {
/* major version change - handle silently */
return 0;
}
if (map->mmap_size < MAIL_INDEX_HEADER_MIN_SIZE) {
mail_index_set_error(index, "Corrupted index file %s: "
"File too small (%"PRIuSIZE_T")",
index->filepath, map->mmap_size);
return 0;
}
map->mmap_used_size = hdr->header_size +
hdr->messages_count * hdr->record_size;
if (map->mmap_used_size > map->mmap_size) {
records_count = (map->mmap_size - hdr->header_size) /
hdr->record_size;
mail_index_set_error(index, "Corrupted index file %s: "
"messages_count too large (%u > %u)",
index->filepath, hdr->messages_count,
records_count);
return 0;
}
mail_index_map_copy_hdr(map, hdr);
map->hdr_base = map->mmap_base;
map->records = PTR_OFFSET(map->mmap_base, map->hdr.header_size);
map->records_count = map->hdr.messages_count;
return 1;
}
static int mail_index_read_header(struct mail_index *index,
void *buf, size_t buf_size, size_t *pos_r)
{
size_t pos;
int ret;
memset(buf, 0, sizeof(struct mail_index_header));
/* try to read the whole header, but it's not necessarily an error to
read less since the older versions of the index format could be
smaller. Request reading up to buf_size, but accept if we only got
the header. */
pos = 0;
do {
ret = pread(index->fd, PTR_OFFSET(buf, pos),
buf_size - pos, pos);
if (ret > 0)
pos += ret;
} while (ret > 0 && pos < sizeof(struct mail_index_header));
*pos_r = pos;
return ret;
}
static int
mail_index_read_map(struct mail_index *index, struct mail_index_map *map,
bool *retry_r, bool try_retry)
{
const struct mail_index_header *hdr;
unsigned char buf[512];
void *data = NULL;
ssize_t ret;
size_t pos, records_size;
i_assert(map->mmap_base == NULL);
*retry_r = FALSE;
ret = mail_index_read_header(index, buf, sizeof(buf), &pos);
hdr = (const struct mail_index_header *)buf;
if (pos > (ssize_t)offsetof(struct mail_index_header, major_version) &&
hdr->major_version != MAIL_INDEX_MAJOR_VERSION) {
/* major version change - handle silently */
return 0;
}
if (ret >= 0 && pos >= MAIL_INDEX_HEADER_MIN_SIZE &&
(ret > 0 || pos >= hdr->base_header_size)) {
if (hdr->base_header_size < MAIL_INDEX_HEADER_MIN_SIZE ||
hdr->header_size < hdr->base_header_size) {
mail_index_set_error(index, "Corrupted index file %s: "
"Corrupted header sizes (base %u, full %u)",
index->filepath, hdr->base_header_size,
hdr->header_size);
return 0;
}
if (pos > hdr->header_size)
pos = hdr->header_size;
/* place the base header into memory. */
buffer_reset(map->hdr_copy_buf);
buffer_append(map->hdr_copy_buf, buf, pos);
if (pos != hdr->header_size) {
/* @UNSAFE: read the rest of the header into memory */
data = buffer_append_space_unsafe(map->hdr_copy_buf,
hdr->header_size -
pos);
ret = pread_full(index->fd, data,
hdr->header_size - pos, pos);
}
}
if (ret > 0) {
/* header read, read the records now. */
records_size = hdr->messages_count * hdr->record_size;
if (map->buffer == NULL) {
map->buffer = buffer_create_dynamic(default_pool,
records_size);
}
/* @UNSAFE */
buffer_set_used_size(map->buffer, 0);
data = buffer_append_space_unsafe(map->buffer, records_size);
ret = pread_full(index->fd, data, records_size,
hdr->header_size);
}
if (ret < 0) {
if (errno == ESTALE && try_retry) {
/* a new index file was renamed over this one. */
*retry_r = TRUE;
return 0;
}
mail_index_set_syscall_error(index, "pread_full()");
return -1;
}
if (ret == 0) {
mail_index_set_error(index,
"Corrupted index file %s: File too small",
index->filepath);
return 0;
}
map->records = data;
map->records_count = hdr->messages_count;
mail_index_map_copy_hdr(map, hdr);
map->hdr_base = map->hdr_copy_buf->data;
index->sync_log_file_seq = hdr->log_file_seq;
index->sync_log_file_offset = hdr->log_file_int_offset;
return 1;
}
bool mail_index_is_ext_synced(struct mail_transaction_log_view *log_view,
struct mail_index_map *map)
{
uint32_t prev_seq;
uoff_t prev_offset;
mail_transaction_log_view_get_prev_pos(log_view, &prev_seq,
&prev_offset);
return prev_seq < map->hdr.log_file_seq ||
(prev_seq == map->hdr.log_file_seq &&
prev_offset < map->hdr.log_file_ext_offset);
}
static int mail_index_sync_from_transactions(struct mail_index *index,
struct mail_index_map **map,
bool sync_to_index)
{
const struct mail_index_header *map_hdr = &(*map)->hdr;
struct mail_index_view *view;
struct mail_transaction_log_view *log_view;
struct mail_index_sync_map_ctx sync_map_ctx;
struct mail_index_header hdr;
const struct mail_transaction_header *thdr;
const void *tdata;
uint32_t prev_seq, max_seq;
uoff_t prev_offset, max_offset;
size_t pos;
int ret;
bool skipped, check_ext_offsets, broken;
if (sync_to_index) {
/* read the real log position where we are supposed to be
synced */
ret = mail_index_read_header(index, &hdr, sizeof(hdr), &pos);
if (ret < 0 && errno != ESTALE) {
mail_index_set_syscall_error(index, "pread()");
return -1;
}
if (pos < MAIL_INDEX_HEADER_MIN_SIZE)
return 0;
if (map_hdr->log_file_seq == hdr.log_file_seq &&
map_hdr->log_file_int_offset == hdr.log_file_int_offset) {
/* nothing to do */
return 1;
}
if (map_hdr->log_file_seq > hdr.log_file_seq ||
(map_hdr->log_file_seq == hdr.log_file_seq &&
map_hdr->log_file_int_offset > hdr.log_file_int_offset)) {
/* we went too far, have to re-read the file */
return 0;
}
if (map_hdr->log_file_ext_offset !=
map_hdr->log_file_int_offset ||
hdr.log_file_ext_offset != hdr.log_file_int_offset) {
/* too much trouble to get this right. */
return 0;
}
max_seq = hdr.log_file_seq;
max_offset = hdr.log_file_int_offset;
} else {
/* sync everything there is */
max_seq = (uint32_t)-1;
max_offset = (uoff_t)-1;
}
log_view = mail_transaction_log_view_open(index->log);
if (mail_transaction_log_view_set(log_view,
map_hdr->log_file_seq,
map_hdr->log_file_int_offset,
max_seq, max_offset,
MAIL_TRANSACTION_TYPE_MASK) <= 0) {
/* can't use it. sync by re-reading index. */
mail_transaction_log_view_close(&log_view);
return 0;
}
index->map = *map;
view = mail_index_view_open(index);
mail_index_sync_map_init(&sync_map_ctx, view,
MAIL_INDEX_SYNC_HANDLER_HEAD);
check_ext_offsets = TRUE; broken = FALSE;
while ((ret = mail_transaction_log_view_next(log_view, &thdr, &tdata,
&skipped)) > 0) {
if ((thdr->type & MAIL_TRANSACTION_EXTERNAL) != 0 &&
check_ext_offsets) {
if (mail_index_is_ext_synced(log_view, index->map))
continue;
check_ext_offsets = FALSE;
}
if (mail_index_sync_record(&sync_map_ctx, thdr, tdata) < 0) {
ret = 0;
broken = TRUE;
break;
}
}
if (ret == 0 && !broken)
ret = 1;
mail_transaction_log_view_get_prev_pos(log_view, &prev_seq,
&prev_offset);
i_assert(prev_seq <= max_seq &&
(prev_seq != max_seq || prev_offset <= max_offset));
index->map->hdr.log_file_seq = prev_seq;
index->map->hdr.log_file_int_offset =
index->map->hdr.log_file_ext_offset = prev_offset;
mail_index_sync_map_deinit(&sync_map_ctx);
mail_index_view_close(&view);
mail_transaction_log_view_close(&log_view);
*map = index->map;
index->map = NULL;
if (sync_to_index && ret > 0) {
/* make sure we did everything right. note that although the
message counts should be equal, the flag counters may not */
i_assert(hdr.messages_count == (*map)->hdr.messages_count);
i_assert(hdr.log_file_seq == (*map)->hdr.log_file_seq);
i_assert(hdr.log_file_int_offset == (*map)->hdr.log_file_int_offset);
i_assert(hdr.log_file_ext_offset == (*map)->hdr.log_file_ext_offset);
}
return ret;
}
static int mail_index_read_map_with_retry(struct mail_index *index,
struct mail_index_map **map,
bool sync_to_index)
{
mail_index_sync_lost_handler_t *const *handlers;
unsigned int i, count;
int ret;
bool retry;
if (index->log_locked) {
/* we're most likely syncing the index and we really don't
want to read more than what was synced last time. */
sync_to_index = TRUE;
}
if ((*map)->hdr.indexid != 0 && index->log != NULL) {
/* we're not creating the index, or opening transaction log.
sync this as a view from transaction log. */
index->sync_update = TRUE;
ret = mail_index_sync_from_transactions(index, map,
sync_to_index);
index->sync_update = FALSE;
if (ret != 0)
return ret;
/* transaction log lost/broken, fallback to re-reading it */
}
/* notify all "sync lost" handlers */
handlers = array_get(&index->sync_lost_handlers, &count);
for (i = 0; i < count; i++)
(*handlers[i])(index);
for (i = 0;; i++) {
ret = mail_index_read_map(index, *map, &retry,
i < MAIL_INDEX_ESTALE_RETRY_COUNT);
if (ret != 0 || !retry)
return ret;
/* ESTALE - reopen index file */
if (close(index->fd) < 0)
mail_index_set_syscall_error(index, "close()");
index->fd = -1;
ret = mail_index_try_open_only(index);
if (ret <= 0) {
if (ret == 0) {
/* the file was lost */
errno = ENOENT;
mail_index_set_syscall_error(index, "open()");
}
return -1;
}
}
/* Too many ESTALE retries */
mail_index_set_syscall_error(index, "read_map()");
return -1;
}
static int mail_index_map_try_existing(struct mail_index_map *map)
{
const struct mail_index_header *hdr;
size_t used_size;
if (MAIL_INDEX_MAP_IS_IN_MEMORY(map))
return 0;
hdr = map->mmap_base;
/* always check corrupted-flag to avoid errors later */
if ((hdr->flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0)
return -1;
used_size = hdr->header_size + hdr->messages_count * hdr->record_size;
if (map->mmap_size >= used_size && map->hdr_base == hdr) {
map->records_count = hdr->messages_count;
mail_index_map_copy_hdr(map, hdr);
return 1;
}
return 0;
}
int mail_index_map(struct mail_index *index, bool force)
{
struct mail_index_map *map;
int ret;
i_assert(!index->mapping);
i_assert(index->map == NULL || index->map->refcount > 0);
i_assert(index->lock_type != F_UNLCK);
if (MAIL_INDEX_IS_IN_MEMORY(index)) {
if (index->map == NULL)
mail_index_create_in_memory(index, NULL);
return 1;
}
index->mapping = TRUE;
if (!force && index->map != NULL) {
i_assert(index->hdr != NULL);
ret = mail_index_map_try_existing(index->map);
if (ret != 0) {
index->mapping = FALSE;
return ret;
}
if (index->lock_type == F_WRLCK) {
/* we're syncing, don't break the mapping */
index->mapping = FALSE;
return 1;
}
}
if (index->map != NULL && index->map->refcount > 1) {
/* this map is already used by some views and they may have
pointers into it. leave them and create a new mapping. */
if (!index->mmap_disable) {
map = NULL;
} else {
/* create a copy of the mapping instead so we don't
have to re-read it */
map = mail_index_map_clone(index->map,
index->map->hdr.record_size);
}
index->map->refcount--;
index->map = NULL;
} else {
map = index->map;
}
if (map == NULL) {
map = i_new(struct mail_index_map, 1);
map->refcount = 1;
map->hdr_copy_buf =
buffer_create_dynamic(default_pool, sizeof(map->hdr));
} else if (MAIL_INDEX_MAP_IS_IN_MEMORY(map)) {
i_assert(!map->write_to_disk);
} else if (map->mmap_base != NULL) {
i_assert(map->buffer == NULL);
if (munmap(map->mmap_base, map->mmap_size) < 0)
mail_index_set_syscall_error(index, "munmap()");
map->mmap_base = NULL;
}
index->hdr = NULL;
index->map = NULL;
if (!index->mmap_disable)
ret = mail_index_mmap(index, map);
else
ret = mail_index_read_map_with_retry(index, &map, force);
i_assert(index->map == NULL);
if (ret > 0) {
ret = mail_index_check_header(index, map);
if (ret < 0)
ret = 0;
else if (ret == 0) {
index->fsck = TRUE;
ret = 1;
}
}
if (ret <= 0) {
mail_index_map_clear(index, map);
mail_index_unmap(index, &map);
index->mapping = FALSE;
return ret;
}
index->hdr = &map->hdr;
index->map = map;
i_assert(map->hdr.messages_count == map->records_count);
index->mapping = FALSE;
return 1;
}
int mail_index_get_latest_header(struct mail_index *index,
struct mail_index_header *hdr_r)
{
size_t pos;
unsigned int i;
int ret;
if (!index->mmap_disable) {
ret = mail_index_map(index, FALSE);
if (ret > 0)
*hdr_r = *index->hdr;
else
memset(hdr_r, 0, sizeof(*hdr_r));
return ret;
}
for (i = 0;; i++) {
ret = mail_index_read_header(index, hdr_r, sizeof(*hdr_r),
&pos);
if (ret <= 0 || errno != ESTALE ||
i == MAIL_INDEX_ESTALE_RETRY_COUNT)
break;
/* ESTALE - reopen index file */
if (close(index->fd) < 0)
mail_index_set_syscall_error(index, "close()");
index->fd = -1;
ret = mail_index_try_open_only(index);
if (ret <= 0) {
if (ret == 0) {
/* the file was lost */
errno = ENOENT;
mail_index_set_syscall_error(index, "open()");
}
return -1;
}
}
if (ret < 0)
mail_index_set_syscall_error(index, "pread_full()");
return ret;
}
struct mail_index_map *
mail_index_map_clone(struct mail_index_map *map, uint32_t new_record_size)
{
struct mail_index_map *mem_map;
struct mail_index_header *hdr;
struct mail_index_ext *extensions;
void *src, *dest;
size_t size, copy_size;
unsigned int i, count;
size = map->records_count * new_record_size;
mem_map = i_new(struct mail_index_map, 1);
mem_map->refcount = 1;
mem_map->buffer = buffer_create_dynamic(default_pool, size);
if (map->hdr.record_size == new_record_size)
buffer_append(mem_map->buffer, map->records, size);
else {
copy_size = I_MIN(map->hdr.record_size, new_record_size);
src = map->records;
for (i = 0; i < map->records_count; i++) {
dest = buffer_append_space_unsafe(mem_map->buffer,
new_record_size);
memcpy(dest, src, copy_size);
src = PTR_OFFSET(src, map->hdr.record_size);
}
}
mem_map->records = buffer_get_modifiable_data(mem_map->buffer, NULL);
mem_map->records_count = map->records_count;
mem_map->hdr_copy_buf =
buffer_create_dynamic(default_pool, map->hdr.header_size);
if (map->hdr.base_header_size < sizeof(*hdr))
buffer_append_zero(mem_map->hdr_copy_buf, sizeof(*hdr));
buffer_write(mem_map->hdr_copy_buf, 0,
&map->hdr, map->hdr.base_header_size);
buffer_append(mem_map->hdr_copy_buf,
CONST_PTR_OFFSET(map->hdr_base,
map->hdr.base_header_size),
map->hdr.header_size - map->hdr.base_header_size);
hdr = buffer_get_modifiable_data(mem_map->hdr_copy_buf, NULL);
if (hdr->base_header_size < sizeof(*hdr))
hdr->base_header_size = sizeof(*hdr);
hdr->record_size = new_record_size;
mem_map->hdr = *hdr;
mem_map->hdr_base = hdr;
/* if we're syncing transaction log into memory and later use the
mapping for updating the index, we need to remember what has
changed */
mem_map->write_atomic = map->write_atomic;
if (map->write_to_disk) {
mem_map->write_seq_first = map->write_seq_first;
mem_map->write_seq_last = map->write_seq_last;
}
/* copy extensions */
if (array_is_created(&map->ext_id_map)) {
count = array_count(&map->ext_id_map);
mail_index_map_init_extbufs(mem_map, count + 2);
array_append_array(&mem_map->extensions, &map->extensions);
array_append_array(&mem_map->ext_id_map, &map->ext_id_map);
/* fix the name pointers to use our own pool */
extensions = array_get_modifiable(&mem_map->extensions, &count);
for (i = 0; i < count; i++) {
extensions[i].name = p_strdup(mem_map->extension_pool,
extensions[i].name);
}
}
return mem_map;
}
int mail_index_map_get_ext_idx(struct mail_index_map *map,
uint32_t ext_id, uint32_t *idx_r)
{
const uint32_t *id;
if (!array_is_created(&map->ext_id_map) ||
ext_id >= array_count(&map->ext_id_map))
return 0;
id = array_idx(&map->ext_id_map, ext_id);
*idx_r = *id;
return *idx_r != (uint32_t)-1;
}
static int mail_index_try_open_only(struct mail_index *index)
{
i_assert(!MAIL_INDEX_IS_IN_MEMORY(index));
/* Note that our caller must close index->fd by itself.
mail_index_reopen() for example wants to revert back to old
index file if opening the new one fails. */
index->fd = nfs_safe_open(index->filepath, O_RDWR);
index->readonly = FALSE;
if (index->fd == -1 && errno == EACCES) {
index->fd = open(index->filepath, O_RDONLY);
index->readonly = TRUE;
}
if (index->fd == -1) {
if (errno != ENOENT)
return mail_index_set_syscall_error(index, "open()");
/* have to create it */
return 0;
}
return 1;
}
static int
mail_index_try_open(struct mail_index *index, unsigned int *lock_id_r)
{
unsigned int lock_id;
int ret;
i_assert(index->fd == -1);
if (lock_id_r != NULL)
*lock_id_r = 0;
if (MAIL_INDEX_IS_IN_MEMORY(index))
return 0;
ret = mail_index_try_open_only(index);
if (ret <= 0)
return ret;
if (mail_index_lock_shared(index, FALSE, &lock_id) < 0) {
(void)close(index->fd);
index->fd = -1;
return -1;
}
ret = mail_index_map(index, FALSE);
if (ret == 0) {
/* it's corrupted - recreate it */
mail_index_unlock(index, lock_id);
if (lock_id_r != NULL)
*lock_id_r = 0;
(void)close(index->fd);
index->fd = -1;
} else {
if (lock_id_r != NULL)
*lock_id_r = lock_id;
else
mail_index_unlock(index, lock_id);
}
return ret;
}
int mail_index_write_base_header(struct mail_index *index,
const struct mail_index_header *hdr)
{
size_t hdr_size;
hdr_size = I_MIN(sizeof(*hdr), hdr->base_header_size);
if (!MAIL_INDEX_MAP_IS_IN_MEMORY(index->map)) {
memcpy(index->map->mmap_base, hdr, hdr_size);
if (msync(index->map->mmap_base, hdr_size, MS_SYNC) < 0)
return mail_index_set_syscall_error(index, "msync()");
index->map->hdr = *hdr;
} else {
if (!MAIL_INDEX_IS_IN_MEMORY(index)) {
if (pwrite_full(index->fd, hdr, hdr_size, 0) < 0) {
mail_index_set_syscall_error(index,
"pwrite_full()");
return -1;
}
}
index->map->hdr = *hdr;
buffer_write(index->map->hdr_copy_buf, 0, hdr, hdr_size);
}
return 0;
}
int mail_index_create_tmp_file(struct mail_index *index, const char **path_r)
{
mode_t old_mask;
const char *path;
int fd;
i_assert(!MAIL_INDEX_IS_IN_MEMORY(index));
path = *path_r = t_strconcat(index->filepath, ".tmp", NULL);
old_mask = umask(0);
fd = open(path, O_RDWR|O_CREAT|O_TRUNC, index->mode);
umask(old_mask);
if (fd == -1)
return mail_index_file_set_syscall_error(index, path, "open()");
if (index->gid != (gid_t)-1 && fchown(fd, (uid_t)-1, index->gid) < 0) {
mail_index_file_set_syscall_error(index, path, "fchown()");
return -1;
}
return fd;
}
static int mail_index_create(struct mail_index *index,
struct mail_index_header *hdr)
{
const char *path;
uint32_t seq;
uoff_t offset;
int ret;
i_assert(!MAIL_INDEX_IS_IN_MEMORY(index));
i_assert(index->lock_type == F_UNLCK);
/* log file lock protects index creation */
if (mail_transaction_log_sync_lock(index->log, &seq, &offset) < 0)
return -1;
ret = mail_index_try_open(index, NULL);
if (ret != 0) {
mail_transaction_log_sync_unlock(index->log);
return ret < 0 ? -1 : 0;
}
/* mark the existing log file as synced */
hdr->log_file_seq = seq;
hdr->log_file_int_offset = offset;
hdr->log_file_ext_offset = offset;
/* create it fully in index.tmp first */
index->fd = mail_index_create_tmp_file(index, &path);
if (index->fd == -1)
ret = -1;
else if (write_full(index->fd, hdr, sizeof(*hdr)) < 0) {
mail_index_file_set_syscall_error(index, path, "write_full()");
ret = -1;
} else {
index->lock_type = F_WRLCK;
ret = mail_index_map(index, FALSE);
index->lock_type = F_UNLCK;
}
if (ret == 0) {
/* it's corrupted even while we just created it,
should never happen unless someone pokes the file directly */
mail_index_set_error(index,
"Newly created index file is corrupted: %s", path);
ret = -1;
}
if (ret < 0) {
if (unlink(path) < 0 && errno != ENOENT) {
mail_index_file_set_syscall_error(index, path,
"unlink()");
}
} else {
/* make it visible to others */
if (rename(path, index->filepath) < 0) {
mail_index_set_error(index, "rename(%s, %s) failed: %m",
path, index->filepath);
ret = -1;
}
}
mail_transaction_log_sync_unlock(index->log);
return ret;
}
static void mail_index_header_init(struct mail_index_header *hdr)
{
time_t now = time(NULL);
i_assert((sizeof(*hdr) % sizeof(uint64_t)) == 0);
memset(hdr, 0, sizeof(*hdr));
hdr->major_version = MAIL_INDEX_MAJOR_VERSION;
hdr->minor_version = MAIL_INDEX_MINOR_VERSION;
hdr->base_header_size = sizeof(*hdr);
hdr->header_size = sizeof(*hdr);
hdr->record_size = sizeof(struct mail_index_record);
#ifndef WORDS_BIGENDIAN
hdr->compat_flags |= MAIL_INDEX_COMPAT_LITTLE_ENDIAN;
#endif
hdr->indexid = now;
hdr->next_uid = 1;
}
static void mail_index_create_in_memory(struct mail_index *index,
const struct mail_index_header *hdr)
{
struct mail_index_header tmp_hdr;
struct mail_index_map tmp_map;
if (hdr == NULL) {
mail_index_header_init(&tmp_hdr);
hdr = &tmp_hdr;
}
memset(&tmp_map, 0, sizeof(tmp_map));
tmp_map.hdr = *hdr;
tmp_map.hdr_base = hdr;
/* a bit kludgy way to do this, but it initializes everything
nicely and correctly */
index->map = mail_index_map_clone(&tmp_map, hdr->record_size);
index->hdr = &index->map->hdr;
}
/* returns -1 = error, 0 = won't create, 1 = ok */
static int mail_index_open_files(struct mail_index *index,
enum mail_index_open_flags flags)
{
struct mail_index_header hdr;
unsigned int lock_id = 0;
int ret;
bool created = FALSE;
ret = mail_index_try_open(index, &lock_id);
if (ret > 0)
hdr = *index->hdr;
else if (ret == 0) {
/* doesn't exist, or corrupted */
if ((flags & MAIL_INDEX_OPEN_FLAG_CREATE) == 0 &&
!MAIL_INDEX_IS_IN_MEMORY(index))
return 0;
mail_index_header_init(&hdr);
index->hdr = &hdr;
} else if (ret < 0)
return -1;
index->indexid = hdr.indexid;
index->log = mail_transaction_log_open_or_create(index);
if (index->log == NULL)
return -1;
if (index->fd == -1) {
if (index->indexid != hdr.indexid) {
/* looks like someone else created the transaction log
before we had the chance. use its indexid so we
don't try to create conflicting ones. */
hdr.indexid = index->indexid;
}
if (lock_id != 0) {
mail_index_unlock(index, lock_id);
lock_id = 0;
}
if (!MAIL_INDEX_IS_IN_MEMORY(index)) {
if (mail_index_create(index, &hdr) < 0) {
/* fallback to in-memory index */
mail_index_move_to_memory(index);
mail_index_create_in_memory(index, &hdr);
}
} else {
mail_index_create_in_memory(index, &hdr);
}
created = TRUE;
}
if (lock_id == 0) {
if (mail_index_lock_shared(index, FALSE, &lock_id) < 0)
return -1;
}
index->cache = created ? mail_cache_create(index) :
mail_cache_open_or_create(index);
mail_index_unlock(index, lock_id);
return 1;
}
int mail_index_open(struct mail_index *index, enum mail_index_open_flags flags,
enum mail_index_lock_method lock_method)
{
int i = 0, ret;
if (index->opened) {
if (index->hdr != NULL &&
(index->hdr->flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0) {
/* corrupted, reopen files */
mail_index_close(index);
} else {
return 1;
}
}
index->filepath = MAIL_INDEX_IS_IN_MEMORY(index) ?
i_strdup("(in-memory index)") :
i_strconcat(index->dir, "/", index->prefix, NULL);
for (;;) {
index->shared_lock_count = 0;
index->excl_lock_count = 0;
index->lock_type = F_UNLCK;
index->lock_id = 2;
index->readonly = FALSE;
index->nodiskspace = FALSE;
index->index_lock_timeout = FALSE;
index->log_locked = FALSE;
index->mmap_disable =
(flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) != 0;
index->mmap_no_write =
(flags & MAIL_INDEX_OPEN_FLAG_MMAP_NO_WRITE) != 0;
index->lock_method = lock_method;
/* don't even bother to handle dotlocking without mmap being
disabled. that combination simply doesn't make any sense */
if (lock_method == MAIL_INDEX_LOCK_DOTLOCK &&
!index->mmap_disable) {
i_fatal("lock_method=dotlock and mmap_disable=no "
"combination isn't supported. "
"You don't _really_ want it anyway.");
}
ret = mail_index_open_files(index, flags);
if (ret <= 0)
break;
index->opened = TRUE;
if (index->fsck) {
index->fsck = FALSE;
ret = mail_index_fsck(index);
if (ret == 0) {
/* completely broken, reopen */
if (i++ < 3)
continue;
/* too many tries */
ret = -1;
}
}
break;
}
if (ret <= 0)
mail_index_close(index);
return ret;
}
void mail_index_close(struct mail_index *index)
{
if (index->log != NULL)
mail_transaction_log_close(&index->log);
if (index->map != NULL)
mail_index_unmap(index, &index->map);
if (index->cache != NULL)
mail_cache_free(&index->cache);
if (index->fd != -1) {
if (close(index->fd) < 0)
mail_index_set_syscall_error(index, "close()");
index->fd = -1;
}
i_free_and_null(index->copy_lock_path);
i_free_and_null(index->filepath);
index->indexid = 0;
index->opened = FALSE;
}
int mail_index_reopen(struct mail_index *index, int fd)
{
struct mail_index_map *old_map;
unsigned int old_shared_locks, old_lock_id, lock_id = 0;
int ret, old_fd, old_lock_type;
i_assert(!MAIL_INDEX_IS_IN_MEMORY(index));
i_assert(index->copy_lock_path == NULL || index->excl_lock_count == 0);
old_map = index->map;
old_fd = index->fd;
old_map->refcount++;
/* new file, new locks. the old fd can keep its locks, they don't
matter anymore as no-one's going to modify the file. */
old_lock_type = index->lock_type;
old_lock_id = index->lock_id;
old_shared_locks = index->shared_lock_count;
if (index->lock_type == F_RDLCK)
index->lock_type = F_UNLCK;
index->lock_id += 2;
index->shared_lock_count = 0;
if (fd != -1) {
index->fd = fd;
ret = 0;
} else {
i_assert(index->excl_lock_count == 0);
ret = mail_index_try_open_only(index);
if (ret > 0)
ret = mail_index_lock_shared(index, FALSE, &lock_id);
else if (ret == 0) {
/* index file is lost */
ret = -1;
}
}
if (ret == 0) {
/* read the new mapping. note that with mmap_disable we want
to keep the old mapping in index->map so we can update it
by reading transaction log. */
if (mail_index_map(index, TRUE) <= 0)
ret = -1;
}
if (lock_id != 0)
mail_index_unlock(index, lock_id);
if (ret == 0) {
mail_index_unmap(index, &old_map);
if (close(old_fd) < 0)
mail_index_set_syscall_error(index, "close()");
} else {
if (index->map != NULL)
mail_index_unmap(index, &index->map);
if (index->fd != -1) {
if (close(index->fd) < 0)
mail_index_set_syscall_error(index, "close()");
}
index->map = old_map;
index->hdr = &index->map->hdr;
index->fd = old_fd;
index->lock_type = old_lock_type;
index->lock_id = old_lock_id;
index->shared_lock_count = old_shared_locks;
}
return ret;
}
int mail_index_reopen_if_needed(struct mail_index *index)
{
struct stat st1, st2;
i_assert(index->copy_lock_path == NULL);
if (MAIL_INDEX_IS_IN_MEMORY(index))
return 0;
if (fstat(index->fd, &st1) < 0) {
if (errno == ESTALE) {
/* deleted, reopen */
if (mail_index_reopen(index, -1) < 0)
return -1;
return 1;
}
return mail_index_set_syscall_error(index, "fstat()");
}
if (nfs_safe_stat(index->filepath, &st2) < 0) {
mail_index_set_syscall_error(index, "stat()");
if (errno != ENOENT)
return -1;
/* lost it? recreate later */
mail_index_mark_corrupted(index);
return -1;
}
if (st1.st_ino != st2.st_ino ||
!CMP_DEV_T(st1.st_dev, st2.st_dev)) {
if (mail_index_reopen(index, -1) < 0)
return -1;
return 1;
} else {
return 0;
}
}
int mail_index_refresh(struct mail_index *index)
{
unsigned int lock_id;
int ret;
if (MAIL_INDEX_IS_IN_MEMORY(index))
return 0;
if (index->excl_lock_count > 0) {
/* we have index exclusively locked, nothing could
have changed. */
return 0;
}
if (!index->mmap_disable) {
/* reopening is all we need */
return mail_index_reopen_if_needed(index);
}
i_assert(!index->mapping);
/* mail_index_map() simply reads latest changes from transaction log,
which makes us fully refreshed. */
if (mail_index_lock_shared(index, TRUE, &lock_id) < 0)
return -1;
ret = mail_index_map(index, FALSE);
mail_index_unlock(index, lock_id);
return ret <= 0 ? -1 : 0;
}
struct mail_cache *mail_index_get_cache(struct mail_index *index)
{
return index->cache;
}
int mail_index_set_error(struct mail_index *index, const char *fmt, ...)
{
va_list va;
i_free(index->error);
if (fmt == NULL)
index->error = NULL;
else {
va_start(va, fmt);
index->error = i_strdup_vprintf(fmt, va);
va_end(va);
i_error("%s", index->error);
}
return -1;
}
void mail_index_set_inconsistent(struct mail_index *index)
{
index->indexid = 0;
}
int mail_index_move_to_memory(struct mail_index *index)
{
struct mail_index_map *map;
int ret = 0;
if (MAIL_INDEX_IS_IN_MEMORY(index))
return 0;
/* set the index as being into memory */
i_free_and_null(index->dir);
if (index->map == NULL) {
/* mbox file was never even opened. just mark it as being in
memory and let the caller re-open the index. */
i_assert(index->fd == -1);
return -1;
}
/* move index map to memory */
map = mail_index_map_clone(index->map, index->map->hdr.record_size);
mail_index_unmap(index, &index->map);
index->map = map;
index->hdr = &map->hdr;
if (index->log != NULL) {
/* move transaction log to memory */
if (mail_transaction_log_move_to_memory(index->log) < 0)
ret = -1;
}
/* close the index file. */
if (close(index->fd) < 0)
mail_index_set_syscall_error(index, "close()");
index->fd = -1;
return ret;
}
void mail_index_mark_corrupted(struct mail_index *index)
{
struct mail_index_header hdr;
mail_index_set_inconsistent(index);
if (index->readonly)
return;
hdr = *index->hdr;
hdr.flags |= MAIL_INDEX_HDR_FLAG_CORRUPTED;
if (mail_index_write_base_header(index, &hdr) == 0) {
if (!MAIL_INDEX_IS_IN_MEMORY(index) && fsync(index->fd) < 0)
mail_index_set_syscall_error(index, "fsync()");
}
}
int mail_index_set_syscall_error(struct mail_index *index,
const char *function)
{
i_assert(function != NULL);
if (ENOSPACE(errno)) {
index->nodiskspace = TRUE;
return -1;
}
return mail_index_set_error(index, "%s failed with index file %s: %m",
function, index->filepath);
}
int mail_index_file_set_syscall_error(struct mail_index *index,
const char *filepath,
const char *function)
{
i_assert(filepath != NULL);
i_assert(function != NULL);
if (ENOSPACE(errno)) {
index->nodiskspace = TRUE;
return -1;
}
return mail_index_set_error(index, "%s failed with file %s: %m",
function, filepath);
}
enum mail_index_error mail_index_get_last_error(struct mail_index *index)
{
if (index->nodiskspace)
return MAIL_INDEX_ERROR_DISKSPACE;
if (index->error != NULL)
return MAIL_INDEX_ERROR_INTERNAL;
return MAIL_INDEX_ERROR_NONE;
}
const char *mail_index_get_error_message(struct mail_index *index)
{
return index->error;
}
void mail_index_reset_error(struct mail_index *index)
{
if (index->error != NULL) {
i_free(index->error);
index->error = NULL;
}
index->nodiskspace = FALSE;
index->index_lock_timeout = FALSE;
}
uint32_t mail_index_uint32_to_offset(uint32_t offset)
{
unsigned char buf[4];
i_assert(offset < 0x40000000);
i_assert((offset & 3) == 0);
offset >>= 2;
buf[0] = 0x80 | ((offset & 0x0fe00000) >> 21);
buf[1] = 0x80 | ((offset & 0x001fc000) >> 14);
buf[2] = 0x80 | ((offset & 0x00003f80) >> 7);
buf[3] = 0x80 | (offset & 0x0000007f);
return *((uint32_t *) buf);
}
uint32_t mail_index_offset_to_uint32(uint32_t offset)
{
const unsigned char *buf = (const unsigned char *) &offset;
if ((offset & 0x80808080) != 0x80808080)
return 0;
return (((uint32_t)buf[3] & 0x7f) << 2) |
(((uint32_t)buf[2] & 0x7f) << 9) |
(((uint32_t)buf[1] & 0x7f) << 16) |
(((uint32_t)buf[0] & 0x7f) << 23);
}