mail-index.c revision 92888ef30960c30ccc9e030fe7eab5d4d04a7d1c
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny/* Copyright (C) 2003-2004 Timo Sirainen */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#include "lib.h"
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#include "buffer.h"
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#include "file-lock.h"
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#include "mmap-util.h"
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#include "read-full.h"
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#include "write-full.h"
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#include "mail-index-private.h"
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#include "mail-transaction-log.h"
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#include <stdio.h>
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#include <stddef.h>
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#include <time.h>
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#include <sys/stat.h>
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zelenystruct mail_index *mail_index_alloc(const char *dir, const char *prefix)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny{
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny struct mail_index *index;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index = i_new(struct mail_index, 1);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->dir = i_strdup(dir);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->prefix = i_strdup(prefix);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->fd = -1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->mode = 0600;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->gid = (gid_t)-1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return index;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny}
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zelenyvoid mail_index_free(struct mail_index *index)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny{
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_close(index);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny i_free(index->error);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny i_free(index->dir);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny i_free(index->prefix);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny i_free(index);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny}
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zelenystatic int mail_index_check_header(struct mail_index *index,
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny struct mail_index_map *map)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny{
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny const struct mail_index_header *hdr = map->hdr;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny unsigned char compat_data[3];
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#ifndef WORDS_BIGENDIAN
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny compat_data[0] = MAIL_INDEX_COMPAT_LITTLE_ENDIAN;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#else
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny compat_data[0] = 0;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny#endif
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny compat_data[1] = sizeof(uoff_t);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny compat_data[2] = sizeof(time_t);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny compat_data[3] = sizeof(keywords_mask_t);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (hdr->major_version != MAIL_INDEX_MAJOR_VERSION) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny /* major version change - handle silently(?) */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return -1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (memcmp(hdr->compat_data, compat_data, sizeof(compat_data)) != 0) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny /* architecture change - handle silently(?) */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return -1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if ((map->hdr->flags & MAIL_INDEX_HDR_FLAG_CORRUPTED) != 0) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny /* either a crash or we've already complained about it */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return -1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny /* following some extra checks that only take a bit of CPU */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (hdr->uid_validity == 0 && hdr->next_uid != 1) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_set_error(index, "Corrupted index file %s: "
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny "uid_validity = 0, next_uid = %u",
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->filepath, hdr->next_uid);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return -1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (hdr->next_uid == 0)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return 0;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (hdr->recent_messages_count > hdr->messages_count ||
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny hdr->seen_messages_count > hdr->messages_count ||
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny hdr->deleted_messages_count > hdr->messages_count)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return 0;
8bbf89c5ab798c112773fe23515c3a9df56dde71Nick Guay if (hdr->first_recent_uid_lowwater > hdr->next_uid ||
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny hdr->first_unseen_uid_lowwater > hdr->next_uid ||
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny hdr->first_deleted_uid_lowwater > hdr->next_uid)
8bbf89c5ab798c112773fe23515c3a9df56dde71Nick Guay return 0;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return 1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny}
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zelenystatic void mail_index_map_clear(struct mail_index *index,
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny struct mail_index_map *map)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny{
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (map->buffer != NULL) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny i_assert(map->mmap_base == NULL);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny buffer_free(map->buffer);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->buffer = NULL;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny } else if (map->mmap_base != NULL) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny i_assert(map->buffer == NULL);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (munmap(map->mmap_base, map->mmap_size) < 0)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_set_syscall_error(index, "munmap()");
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->mmap_base = NULL;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (map->refcount > 0) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->mmap_size = 0;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->mmap_used_size = 0;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->hdr = NULL;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->records = NULL;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->records_count = 0;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny}
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zelenyvoid mail_index_unmap(struct mail_index *index, struct mail_index_map *map)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny{
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (--map->refcount > 0)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny i_assert(map->refcount == 0);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_map_clear(index, map);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny i_free(map);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny}
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zelenystatic void mail_index_unmap_forced(struct mail_index *index,
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny struct mail_index_map *map)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny{
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_map_clear(index, map);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_unmap(index, map);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny}
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidekstatic int mail_index_mmap(struct mail_index *index, struct mail_index_map *map)
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidek{
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidek const struct mail_index_header *hdr;
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidek unsigned int records_count;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny i_assert(map->buffer == NULL);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->mmap_base = index->lock_type != F_WRLCK ?
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mmap_ro_file(index->fd, &map->mmap_size) :
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mmap_rw_file(index->fd, &map->mmap_size);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (map->mmap_base == MAP_FAILED) {
894d18ff4178f40a18bbfece8fae270d8307eac6Jakub Hrozek map->mmap_base = NULL;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_set_syscall_error(index, "mmap()");
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return -1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
894d18ff4178f40a18bbfece8fae270d8307eac6Jakub Hrozek if (map->mmap_size < MAIL_INDEX_HEADER_MIN_SIZE) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_set_error(index, "Corrupted index file %s: "
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny "File too small (%"PRIuSIZE_T")",
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->filepath, map->mmap_size);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return 0;
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidek }
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidek
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidek hdr = map->mmap_base;
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidek map->mmap_used_size = hdr->header_size +
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidek hdr->messages_count * sizeof(struct mail_index_record);
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidek
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidek if (map->mmap_used_size > map->mmap_size) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny records_count = (map->mmap_size - hdr->header_size) /
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny sizeof(struct mail_index_record);
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidek mail_index_set_error(index, "Corrupted index file %s: "
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidek "messages_count too large (%u > %u)",
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidek index->filepath, map->hdr->messages_count,
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny records_count);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return 0;
3a59cbd0b7b9c5dd3c62ac1679876070c264d80fMichal Zidek }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->hdr = hdr;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (map->hdr->header_size < sizeof(*map->hdr)) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny /* header smaller than ours, make a copy so our newer headers
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny won't have garbage in them */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny memcpy(&map->hdr_copy, map->hdr, map->hdr->header_size);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->hdr = &map->hdr_copy;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->records = PTR_OFFSET(map->mmap_base, map->hdr->header_size);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->records_count = map->hdr->messages_count;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return 1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny}
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zelenystatic int mail_index_read_map(struct mail_index *index,
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny struct mail_index_map *map)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny{
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny struct mail_index_header hdr;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny void *data = NULL;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny ssize_t ret;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny size_t pos, records_size;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny i_assert(map->mmap_base == NULL);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny memset(&hdr, 0, sizeof(hdr));
894d18ff4178f40a18bbfece8fae270d8307eac6Jakub Hrozek
894d18ff4178f40a18bbfece8fae270d8307eac6Jakub Hrozek ret = 1;
894d18ff4178f40a18bbfece8fae270d8307eac6Jakub Hrozek for (pos = 0; ret > 0 && pos < sizeof(hdr); ) {
894d18ff4178f40a18bbfece8fae270d8307eac6Jakub Hrozek ret = pread(index->fd, PTR_OFFSET(&hdr, pos),
894d18ff4178f40a18bbfece8fae270d8307eac6Jakub Hrozek sizeof(hdr) - pos, pos);
894d18ff4178f40a18bbfece8fae270d8307eac6Jakub Hrozek if (ret > 0)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny pos += ret;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (ret >= 0 && pos >= MAIL_INDEX_HEADER_MIN_SIZE) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny records_size = hdr.messages_count *
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny sizeof(struct mail_index_record);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (map->buffer == NULL) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->buffer = buffer_create_dynamic(default_pool,
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny records_size,
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny (size_t)-1);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny /* @UNSAFE */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny buffer_set_used_size(map->buffer, 0);
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek data = buffer_append_space_unsafe(map->buffer, records_size);
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek ret = pread_full(index->fd, data, records_size,
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek hdr.header_size);
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek }
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek if (ret < 0) {
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek if (errno == ESTALE)
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek return 0;
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek mail_index_set_syscall_error(index, "pread_full()");
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek return -1;
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek }
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek if (ret == 0) {
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek mail_index_set_error(index,
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek "Unexpected EOF while reading index file");
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return -1;
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek }
ecfd767c65c39414a86937380b9986c6d2e0aecfJakub Hrozek
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->records = data;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->records_count = hdr.messages_count;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->hdr_copy = hdr;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->hdr = &map->hdr_copy;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return 1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny}
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zelenystatic int mail_index_read_map_with_retry(struct mail_index *index,
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny struct mail_index_map *map)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny{
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny int i, ret;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny for (i = 0; i < MAIL_INDEX_ESTALE_RETRY_COUNT; i++) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny ret = mail_index_read_map(index, map);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (ret != 0)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return ret;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny /* ESTALE - reopen index file */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (close(index->fd) < 0)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_set_syscall_error(index, "close()");
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->fd = -1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny ret = mail_index_try_open_only(index);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (ret <= 0) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (ret == 0) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny /* the file was lost */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny errno = ENOENT;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_set_syscall_error(index, "open()");
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return -1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny /* Too many ESTALE retries */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_set_syscall_error(index, "read_map()");
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return -1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny}
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zelenyint mail_index_map(struct mail_index *index, int force)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny{
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny const struct mail_index_header *hdr;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny struct mail_index_map *map;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny size_t used_size;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny int ret;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map = index->map;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (map == NULL) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map = i_new(struct mail_index_map, 1);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->refcount = 1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny } else if (MAIL_INDEX_MAP_IS_IN_MEMORY(map)) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (map->write_to_disk) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny /* we have modified this mapping and it's waiting to
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny be written to disk once we drop exclusive lock.
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mapping couldn't have changed, so do nothing. */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return 1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny /* FIXME: we need to re-read header */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny } else if (map->mmap_base != NULL) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny /* see if re-mmaping is needed (file has grown) */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny hdr = map->mmap_base;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny used_size = hdr->header_size +
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny hdr->messages_count * sizeof(struct mail_index_record);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (map->mmap_size >= used_size && !force) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->records_count = hdr->messages_count;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return 1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (munmap(map->mmap_base, map->mmap_size) < 0)
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny mail_index_set_syscall_error(index, "munmap()");
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->mmap_base = NULL;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->hdr = NULL;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->map = NULL;
f004e23af14fe020d81b8f97f30b448105b79606Jakub Hrozek
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (!index->mmap_disable) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if ((ret = mail_index_mmap(index, map)) <= 0) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_unmap_forced(index, map);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return ret;
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny }
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny } else {
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny if (mail_index_read_map_with_retry(index, map) < 0) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_unmap_forced(index, map);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return -1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny ret = mail_index_check_header(index, map);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (ret < 0) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_unmap_forced(index, map);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return 0;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (ret == 0)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->fsck = TRUE;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->log_file_seq = map->hdr->log_file_seq;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->log_file_offset = map->hdr->log_file_offset;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->hdr = map->hdr;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->map = map;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return 1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny}
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zelenystruct mail_index_map *mail_index_map_to_memory(struct mail_index_map *map)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny{
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny const struct mail_index_header *hdr;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny struct mail_index_map *mem_map;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny size_t size;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (MAIL_INDEX_MAP_IS_IN_MEMORY(map)) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny map->refcount++;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return map;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny size = map->records_count * sizeof(struct mail_index_record);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mem_map = i_new(struct mail_index_map, 1);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mem_map->refcount = 1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mem_map->buffer = buffer_create_dynamic(default_pool, size, (size_t)-1);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny buffer_append(mem_map->buffer, map->records, size);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mem_map->records = buffer_get_modifyable_data(mem_map->buffer, NULL);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mem_map->records_count = map->records_count;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny hdr = map->mmap_base;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny memcpy(&mem_map->hdr_copy, map->mmap_base,
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny I_MIN(hdr->header_size, sizeof(mem_map->hdr_copy)));
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mem_map->hdr = &mem_map->hdr_copy;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return mem_map;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny}
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zelenyvoid mail_index_header_init(struct mail_index_header *hdr)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny{
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny time_t now = time(NULL);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny memset(hdr, 0, sizeof(*hdr));
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny hdr->major_version = MAIL_INDEX_MAJOR_VERSION;
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny hdr->minor_version = MAIL_INDEX_MINOR_VERSION;
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny hdr->header_size = sizeof(*hdr);
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny#ifndef WORDS_BIGENDIAN
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny hdr->compat_data[0] = MAIL_INDEX_COMPAT_LITTLE_ENDIAN;
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny#endif
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny hdr->compat_data[1] = sizeof(uoff_t);
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny hdr->compat_data[2] = sizeof(time_t);
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny hdr->compat_data[3] = sizeof(keywords_mask_t);
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny hdr->indexid = now;
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny hdr->next_uid = 1;
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny}
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zelenyint mail_index_write_header(struct mail_index *index,
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny const struct mail_index_header *hdr)
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny{
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny if (!MAIL_INDEX_MAP_IS_IN_MEMORY(index->map)) {
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny memcpy(index->map->mmap_base, hdr, sizeof(*hdr));
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny if (msync(index->map->mmap_base, sizeof(*hdr), MS_SYNC) < 0)
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny return mail_index_set_syscall_error(index, "msync()");
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny } else {
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny if (pwrite_full(index->fd, hdr, sizeof(*hdr), 0) < 0) {
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny mail_index_set_syscall_error(index, "pwrite_full()");
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny return -1;
1a3e6221b38a7cae27d7e84a30bb8ea3c3900a47Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->map->hdr_copy = *hdr;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->hdr = &index->map->hdr_copy;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return 0;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny}
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zelenyint mail_index_create_tmp_file(struct mail_index *index, const char **path_r)
21b3c8aff20d1987add2a93561653a3d6a9685d4Jakub Hrozek{
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny const char *path;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny int fd;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny path = *path_r = t_strconcat(index->filepath, ".tmp", NULL);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny fd = open(path, O_RDWR|O_CREAT|O_TRUNC, index->mode);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (fd == -1)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return mail_index_file_set_syscall_error(index, path, "open()");
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (index->gid != (gid_t)-1 &&
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny fchown(index->fd, (uid_t)-1, index->gid) < 0) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_file_set_syscall_error(index, path, "fchown()");
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return -1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return fd;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny}
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zelenystatic int mail_index_create(struct mail_index *index,
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny struct mail_index_header *hdr)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny{
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny const char *path;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny uint32_t seq;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny uoff_t offset;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny int ret;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny /* log file lock protects index creation */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (mail_transaction_log_sync_lock(index->log, &seq, &offset) < 0)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return -1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny hdr->log_file_seq = seq;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny hdr->log_file_offset = offset;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny ret = mail_index_try_open(index, NULL);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (ret != 0) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_transaction_log_sync_unlock(index->log);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny return ret;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny /* create it fully in index.tmp first */
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny index->fd = mail_index_create_tmp_file(index, &path);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny if (index->fd == -1)
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny ret = -1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny else if (write_full(index->fd, hdr, sizeof(*hdr)) < 0) {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny mail_index_file_set_syscall_error(index, path, "write_full()");
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny ret = -1;
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny } else {
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny ret = mail_index_map(index, FALSE);
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny }
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny
4c11f752e1f10cf5740d53a3206bb795e9e34fe8Jan Zeleny 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()");
}
return -1;
}
/* make it visible to others */
if (rename(path, index->filepath) < 0) {
mail_index_set_error(index, "rename(%s, %s) failed: %m",
path, index->filepath);
return -1;
}
mail_transaction_log_sync_unlock(index->log);
return 1;
}
int mail_index_try_open_only(struct mail_index *index)
{
int i;
for (i = 0; i < 3; i++) {
index->fd = open(index->filepath, O_RDWR);
if (index->fd == -1 && errno == EACCES) {
index->fd = open(index->filepath, O_RDONLY);
index->readonly = TRUE;
}
if (index->fd != -1 || errno != ESTALE)
break;
/* May happen with some OSes with NFS. Try again, although
there's still a race condition with another computer
creating the index file again. However, we can't try forever
as ESTALE happens also if index directory has been deleted
from server.. */
}
if (index->fd == -1) {
if (errno != ENOENT)
return mail_index_set_syscall_error(index, "open()");
/* have to create it */
return 0;
}
return 1;
}
int mail_index_try_open(struct mail_index *index, unsigned int *lock_id_r)
{
unsigned int lock_id;
int ret;
if (lock_id_r != NULL)
*lock_id_r = 0;
ret = mail_index_try_open_only(index);
if (ret <= 0)
return ret;
if (mail_index_lock_shared(index, FALSE, &lock_id) < 0)
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;
}
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;
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)
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 (lock_id != 0)
mail_index_unlock(index, lock_id);
return index->fd != -1 ? 1 : mail_index_create(index, &hdr);
}
int mail_index_open(struct mail_index *index, enum mail_index_open_flags flags)
{
int i = 0, ret;
if (index->opened)
return 0;
index->filepath = i_strconcat(index->dir, "/", index->prefix, NULL);
do {
index->shared_lock_count = 0;
index->excl_lock_count = 0;
index->lock_type = F_UNLCK;
index->lock_id = 2;
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->fcntl_locks_disable =
(flags & MAIL_INDEX_OPEN_FLAG_FCNTL_LOCKS_DISABLE) != 0;
index->readonly = FALSE;
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;
} while (1);
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);
index->log = NULL;
}
if (index->map != NULL) {
mail_index_unmap(index, index->map);
index->map = NULL;
}
if (index->fd != -1) {
if (close(index->fd) < 0)
mail_index_set_syscall_error(index, "close()");
index->fd = -1;
}
i_free(index->copy_lock_path);
index->copy_lock_path = NULL;
i_free(index->filepath);
index->filepath = NULL;
index->indexid = 0;
index->opened = FALSE;
}
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;
}
void mail_index_mark_corrupted(struct mail_index *index)
{
struct mail_index_header hdr;
mail_index_set_inconsistent(index);
if (index->readonly)
return;
/* make sure we can write the header */
if (!MAIL_INDEX_MAP_IS_IN_MEMORY(index->map)) {
if (mprotect(index->map->mmap_base, sizeof(hdr),
PROT_READ | PROT_WRITE) < 0) {
mail_index_set_syscall_error(index, "mprotect()");
return;
}
}
hdr = *index->hdr;
hdr.flags |= MAIL_INDEX_HDR_FLAG_CORRUPTED;
if (mail_index_write_header(index, &hdr) == 0) {
if (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;
}
int mail_index_is_in_memory(struct mail_index *index)
{
return FALSE; // FIXME
}