mail-index-map.c revision 648d24583c1574441c4fa0331a90bd4d6e7996c5
/* Copyright (C) 2003-2007 Timo Sirainen */
#include "lib.h"
#include "array.h"
#include "str-sanitize.h"
#include "nfs-workarounds.h"
#include "mmap-util.h"
#include "read-full.h"
#include "mail-index-private.h"
#include "mail-index-sync-private.h"
#include "mail-transaction-log-private.h"
unsigned int initial_count)
{
#define EXTENSION_NAME_APPROX_LEN 20
#define EXT_GLOBAL_ALLOC_SIZE \
#define EXT_PER_ALLOC_SIZE \
sizeof(struct mail_index_ext) + sizeof(uint32_t))
pool_alloconly_create("map extensions",
} else {
/* try to use the existing pool's size for initial_count so
we don't grow it unneededly */
}
}
}
{
const struct mail_index_ext *extensions;
unsigned int i, size;
for (i = 0; i < size; i++) {
*idx_r = i;
return TRUE;
}
}
}
return FALSE;
}
unsigned int mail_index_map_ext_hdr_offset(unsigned int name_len)
{
return MAIL_INDEX_HEADER_SIZE_ALIGN(size);
}
{
struct mail_index_ext *ext;
idx = 0;
} else {
}
/* Update index ext_id -> map ext_id mapping. Fill non-used
ext_ids with (uint32_t)-1 */
return idx;
}
unsigned int *offset_p,
const struct mail_index_ext_header **ext_hdr_r,
const char **name_r)
{
const struct mail_index_ext_header *ext_hdr;
unsigned int offset, name_offset;
*name_r = "";
/* Extension header contains:
- struct mail_index_ext_header
- name (not 0-terminated)
- 64bit alignment padding
- extension header contents
- 64bit alignment padding
*/
return -1;
return -1;
/* we allow only plain ASCII names, so this extension
is most likely broken */
*name_r = "";
}
/* finally make sure that the hdr_size is small enough.
do this last so that we could return a usable name. */
return -1;
return 0;
}
const struct mail_index_ext_header *ext_hdr,
{
*error_r = "Invalid field values";
return -1;
}
if (*name == '\0') {
*error_r = "Broken name";
return -1;
}
"outside record size (%u+%u > %u)",
hdr->record_size);
return -1;
}
if (ext_hdr->record_size > 0 &&
return -1;
}
return 0;
}
{
const struct mail_index_ext_header *ext_hdr;
/* extension headers always start from 64bit offsets, so if base header
doesn't happen to be 64bit aligned we'll skip some bytes */
/* nothing to do, skip allocatations and all */
return 0;
}
for (i = 0; i < old_count; i++)
ext_offset = offset;
t_push();
"Header extension #%d (%s) goes outside header",
t_pop();
return -1;
}
"Broken extension #%d (%s): %s",
t_pop();
return -1;
}
"Duplicate header extension %s",
t_pop();
return -1;
}
t_pop();
}
return 0;
}
{
const struct mail_index_ext *ext;
const struct mail_index_keyword_header *kw_hdr;
const struct mail_index_keyword_header_rec *kw_rec;
const char *name;
unsigned int i, name_area_end_offset, old_count;
return 0;
}
/* Extension header contains:
- struct mail_index_keyword_header
- struct mail_index_keyword_header_rec * keywords_count
- const char names[] * keywords_count
*/
/* Keywords can only be added into same mapping. Removing requires a
new mapping (recreating the index file) */
/* nothing changed */
return 0;
}
/* make sure the header is valid */
"Keywords removed unexpectedly",
return -1;
}
"keywords_count larger than header size",
return -1;
}
for (i = 0; i < kw_hdr->keywords_count; i++) {
"name_offset points outside allocated header",
return -1;
}
}
"Keyword header doesn't end with NUL",
return -1;
}
/* create file -> index mapping */
#ifdef DEBUG
/* Check that existing headers are still the same. It's behind DEBUG
since it's pretty useless waste of CPU normally. */
const unsigned int *old_idx;
unsigned int idx;
"Keywords changed unexpectedly",
return -1;
}
}
#endif
/* Register the newly seen keywords */
for (; i < kw_hdr->keywords_count; i++) {
unsigned int idx;
if (*keyword == '\0') {
"Empty keyword name in header",
return -1;
}
}
return 0;
}
const struct mail_index_header *hdr,
{
enum mail_index_header_compat_flags compat_flags = 0;
#ifndef WORDS_BIGENDIAN
#endif
/* major version change - handle silently(?) */
return FALSE;
}
/* we've already complained about it */
return FALSE;
}
/* architecture change */
"CPU architecture changed",
return FALSE;
}
"Corrupted header sizes (base %u, full %u)",
hdr->header_size);
return FALSE;
}
return FALSE;
}
"indexid changed: %u -> %u",
}
}
return TRUE;
}
{
return -1;
/* following some extra checks that only take a bit of CPU */
"record_size too small: %u < %"PRIuSIZE_T,
sizeof(struct mail_index_record));
return -1;
}
return 0;
return 0;
return 0;
return 0;
if (hdr->first_recent_uid == 0 ||
return 0;
if (hdr->messages_count > 0) {
/* last message's UID must be smaller than next_uid.
also make sure it's not zero. */
const struct mail_index_record *rec;
return 0;
}
return 1;
}
const struct mail_index_header *hdr)
{
/* header smaller than ours, make a copy so our newer headers
won't have garbage in them */
} else {
}
/* FIXME: backwards compatibility, remove later. In case this index is
accessed with Dovecot v1.0, avoid recent message counter errors. */
}
{
const struct mail_index_header *hdr;
if (file_size > SSIZE_T_MAX) {
/* too large file to map into memory */
return -1;
}
return -1;
}
/* major version change - handle silently */
return 0;
}
return 0;
}
/* Can't use this file */
return 0;
}
else {
"messages_count too large (%u > %u)",
}
return 1;
}
{
int ret;
/* 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 {
if (ret > 0)
return ret;
}
static int
{
const struct mail_index_header *hdr;
unsigned char read_buf[4096];
const void *buf;
unsigned int records_count = 0, extra;
/* major version change - handle silently */
return 0;
}
/* Can't use this file */
return 0;
}
/* place the base header into memory. */
/* @UNSAFE: read the rest of the header into memory */
hdr->header_size -
pos);
}
}
if (ret > 0) {
/* header read, read the records now. */
(hdr->record_size != 0 &&
"messages_count too large (%u > %u)",
}
}
/* @UNSAFE */
extra = 0;
else {
extra);
}
if (records_size > extra) {
records_size - extra);
}
}
if (ret < 0) {
/* a new index file was renamed over this one. */
return 0;
}
return -1;
}
if (ret == 0) {
"Corrupted index file %s: File too small",
return 0;
}
return 1;
}
unsigned int *lock_id)
{
mail_index_sync_lost_handler_t *const *handlers;
unsigned int i, count;
int ret;
/* notify all "sync lost" handlers */
for (i = 0; i < count; i++)
for (i = 0;; i++) {
/* fstat() below failed */
ret = 0;
} else {
}
break;
/* ESTALE - reopen index file */
*lock_id = 0;
if (ret <= 0) {
if (ret == 0) {
/* the file was lost */
}
return -1;
}
return -1;
else {
return -1;
}
}
}
return ret;
}
struct mail_index_header *hdr)
{
#ifndef WORDS_BIGENDIAN
#endif
}
{
struct mail_index_map tmp_map;
/* a bit kludgy way to do this, but it initializes everything
nicely and correctly */
return mail_index_map_clone(&tmp_map);
}
{
unsigned int lock_id;
bool use_mmap;
if (ret <= 0) {
if (ret < 0)
return -1;
build it from the transaction log. */
return 0;
}
/* the index file is still open, lock it */
return -1;
else {
return -1;
}
}
/* mmaping seems to be slower than just reading the file, so even if
mmap isn't disabled don't use it unless the file is large enough */
if (use_mmap) {
} else {
}
/* make sure the header is ok before using this mapping */
if (ret > 0) {
if (mail_index_map_parse_extensions(new_map) < 0)
ret = 0;
else if (mail_index_map_parse_keywords(new_map) < 0)
ret = 0;
}
break;
/* fsck and try again */
if (mail_index_fsck(index) < 0) {
ret = -1;
break;
}
/* fsck replaced the map */
}
if (ret <= 0) {
return ret;
}
return 1;
}
{
int ret;
/* first try updating the existing mapping from transaction log. */
/* we're not creating the index, or opening transaction log.
sync this as a view from transaction log. */
} else {
ret = 0;
}
if (ret == 0) {
/* try to open and read the latest index. if it fails for
any reason, we'll fallback to updating the existing mapping
from transaction logs (which we'll also do even if the
reopening succeeds) */
(void)mail_index_map_latest_file(index);
/* if we're creating the index file, we don't have any
logs yet */
/* and update the map with the latest changes from
transaction log */
}
}
return ret;
}
struct mail_index_record_map *rec_map)
{
}
}
{
struct mail_index_map *const *maps;
unsigned int i, count;
for (i = 0; i < count; i++) {
if (i == 0 && count == 1)
return;
}
}
i_unreached();
}
{
return;
}
const struct mail_index_record_map *src,
unsigned int record_size)
{
/* if the map is ever written back to disk, we need to keep track of
what has changed. */
}
const struct mail_index_map *src)
{
/* use src->hdr copy directly, because if we got here
from syncing it has the latest changes. */
return;
} else {
dest->hdr_copy_buf =
}
}
}
static struct mail_index_record_map *
{
struct mail_index_record_map *rec_map;
return rec_map;
}
{
struct mail_index_map *mem_map;
struct mail_index_ext *extensions;
unsigned int i, count;
} else {
}
/* copy extensions */
/* fix the name pointers to use our own pool */
for (i = 0; i < count; i++) {
extensions[i].record_size <=
extensions[i].name);
}
}
/* copy keyword map */
&map->keyword_idx_map);
}
return mem_map;
}
{
struct mail_index_record_map *new_map;
return;
}
{
struct mail_index_record_map *new_map;
return;
} else {
}
}
{
return FALSE;
}