/* Copyright (c) 2004-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "buffer.h"
#include "hash.h"
#include "file-cache.h"
#include "read-full.h"
#include "write-full.h"
#include "mmap-util.h"
#include "mail-cache-private.h"
#include <stddef.h>
{
switch (type) {
case MAIL_CACHE_FIELD_BITMASK:
return TRUE;
case MAIL_CACHE_FIELD_STRING:
case MAIL_CACHE_FIELD_HEADER:
return FALSE;
case MAIL_CACHE_FIELD_COUNT:
break;
}
i_unreached();
return FALSE;
}
{
switch (type & ~MAIL_CACHE_DECISION_FORCED) {
case MAIL_CACHE_DECISION_NO:
case MAIL_CACHE_DECISION_TEMP:
case MAIL_CACHE_DECISION_YES:
return TRUE;
default:
return FALSE;
}
}
{
return -1;
}
return -1;
}
return 0;
}
static void
const struct mail_cache_field *newfield)
{
bool initial_registering;
/* are we still doing the initial cache field registering for
internal fields and for mail_*cache_fields settings? */
if (!initial_registering)
}
if (!initial_registering)
}
if (orig->decision_dirty)
}
struct mail_cache_field *fields,
unsigned int fields_count)
{
char *name;
void *value;
unsigned int new_idx;
unsigned int i, j, registered_count;
for (i = 0; i < fields_count; i++) {
continue;
}
/* check if the same header is being registered in the
same field array */
for (j = 0; j < i; j++) {
break;
}
}
if (j == i)
}
return;
/* @UNSAFE */
struct mail_cache_field_private,
for (i = 0; i < fields_count; i++) {
if (idx < registered_count)
continue;
/* new index - save it */
POINTER_CAST(idx));
}
}
unsigned int
{
char *key;
void *value;
return POINTER_CAST_TO(value, unsigned int);
else
return UINT_MAX;
}
const struct mail_cache_field *
{
}
struct mail_cache_field *
unsigned int *count_r)
{
unsigned int i;
(void)mail_cache_open_and_verify(cache);
for (i = 0; i < cache->fields_count; i++) {
}
return list;
}
static int
const struct mail_cache_header_fields **field_hdr_r)
{
const void *data;
unsigned int next_count = 0;
int ret;
if (MAIL_CACHE_IS_UNUSABLE(cache)) {
*offset_r = 0;
if (field_hdr_r != NULL)
*field_hdr_r = NULL;
return 0;
}
/* find the latest header */
offset = 0;
while (next_offset != 0) {
if (next_offset == offset) {
"next_offset in field header loops");
return -1;
}
/* In Dovecot v2.2+ we don't try to use any holes,
so next_offset must always be larger than current offset.
also makes it easier to guarantee there aren't any loops
(which we don't bother doing for old files) */
"next_offset in field header decreases");
return -1;
}
&data);
if (ret <= 0) {
if (ret < 0)
return -1;
"header field next_offset points outside file");
return -1;
}
} else {
/* if we need to follow multiple offsets to get to
the last one, it's faster to just pread() the file
instead of going through cache */
sizeof(tmp_field_hdr), offset);
if (ret < 0) {
return -1;
}
if (ret == 0) {
"header field next_offset points outside file");
return -1;
}
}
next_count++;
}
if (offset == 0) {
return -1;
}
if (field_hdr_r != NULL) {
/* detect corrupted size later */
/* invalidate the cache fields area to make sure we
}
if (ret < 0)
return -1;
if (ret == 0) {
"header field size outside file");
return -1;
}
*field_hdr_r = data;
}
return 0;
}
{
char *orig_key;
void *orig_value;
return -1;
if (offset == 0) {
/* no fields - the file is empty */
return 0;
}
/* check the fixed size of the header. name[] has to be checked
separately */
return -1;
}
if (new_fields_count != 0) {
} else {
}
/* clear the old mapping */
for (i = 0; i < cache->fields_count; i++)
for (i = 0; i < field_hdr->fields_count; i++) {
"field header names corrupted");
return -1;
}
if (types[i] > MAIL_CACHE_FIELD_COUNT) {
return -1;
}
if (!field_decision_is_valid(decisions[i])) {
"field decision type corrupted");
return -1;
}
/* ignore any forced-flags in the file */
&orig_key, &orig_value)) {
/* already exists, see if decision can be updated */
if ((cur_dec & MAIL_CACHE_DECISION_FORCED) != 0) {
/* Forced decision. If the decision has
changed, update the fields in the file. */
/* Decisions have recently been updated
internally. Don't change them. */
} else {
/* Use the decision from the cache file. */
}
return -1;
} else {
/* field is currently unknown, so just use whatever
exists in the file. */
}
"Duplicated field in header: %s", names);
return -1;
}
/* update last_used if it's newer than ours */
(dec & MAIL_CACHE_DECISION_FORCED) == 0 &&
dec != MAIL_CACHE_DECISION_NO) {
/* time to drop this field. don't bother dropping
fields that have never been used. */
}
names = p + 1;
}
return 0;
}
{
const void *data;
unsigned int i, field;
/* copy the existing fields */
for (i = 0; i < cache->file_fields_count; i++) {
}
if (!add_new)
return;
/* copy newly wanted fields */
for (i = 0; i < cache->fields_count; i++) {
if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i)) {
}
}
}
{
const int *data;
unsigned int i, field;
/* copy the existing fields */
for (i = 0; i < cache->file_fields_count; i++) {
}
if (!add_new)
return;
/* copy newly wanted fields */
for (i = 0; i < cache->fields_count; i++) {
if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i)) {
}
}
}
{
int ret = 0;
if (mail_cache_header_fields_read(cache) < 0 ||
return -1;
sizeof(uint32_t));
if (ret == 0) {
dec_offset = offset +
if (ret == 0) {
for (i = 0; i < cache->file_fields_count; i++)
}
}
if (ret == 0)
return ret;
}
{
int ret;
T_BEGIN {
} T_END;
return ret;
}
if (mail_cache_lock(cache) <= 0)
return -1;
T_BEGIN {
} T_END;
if (mail_cache_unlock(cache) < 0)
ret = -1;
return ret;
}
{
unsigned int field;
const char *name;
uint32_t i;
for (i = 0; i < cache->fields_count; i++) {
if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i))
hdr.fields_count++;
}
/* we have to keep the field order for the existing fields. */
sizeof(uint32_t));
sizeof(uint32_t));
/* add existing fields' names */
for (i = 0; i < cache->file_fields_count; i++) {
}
/* add newly wanted fields' names */
for (i = 0; i < cache->fields_count; i++) {
if (CACHE_FIELD_IS_NEWLY_WANTED(cache, i)) {
}
}
}
{
return -1;
if (*offset_r == 0) {
} else {
}
return 0;
}