mail-cache-transaction.c revision 72cbf33ae81fde08384d30c779ff540752d9256c
/* Copyright (C) 2003-2004 Timo Sirainen */
#include "lib.h"
#include "buffer.h"
#include "file-set-size.h"
#include "read-full.h"
#include "write-full.h"
#include "mail-cache-private.h"
#include "mail-index-transaction-private.h"
#include <stddef.h>
struct mail_cache_transaction_ctx {
struct mail_cache *cache;
struct mail_cache_view *view;
struct mail_index_transaction *trans;
unsigned int next_unused_header_lowwater;
enum mail_cache_field fields;
unsigned int changes:1;
};
static const unsigned char *null4[] = { 0, 0, 0, 0 };
struct mail_cache_transaction_ctx *
struct mail_index_transaction *t)
{
struct mail_cache_transaction_ctx *ctx;
if (t->cache_trans_ctx != NULL)
return t->cache_trans_ctx;
ctx->cache_data =
ctx->reservations =
t->cache_trans_ctx = ctx;
return ctx;
}
{
}
{
/* grow the file */
if (grow_size < 16384)
grow_size = 16384;
new_fsize &= ~1023;
return -1;
}
return -1;
}
}
return 0;
}
struct mail_cache_hole_header *hole_r)
{
struct mail_cache_hole_header hole;
while (offset != 0) {
return FALSE;
}
"Invalid magic in hole header");
return FALSE;
}
break;
}
if (offset == 0)
return FALSE;
if (prev_offset == 0)
else {
return FALSE;
}
}
return TRUE;
}
static void
{
sizeof(ctx->reserved_space_offset));
sizeof(ctx->reserved_space));
}
static int
{
struct mail_cache_hole_header hole;
/* found a large enough hole. */
return 0;
}
if (MAIL_CACHE_IS_UNUSABLE(cache)) {
/* mail_cache_unlink_hole() could have noticed corruption */
return -1;
}
return -1;
}
if (!commit) {
}
return -1;
hdr->used_file_size) {
/* we can simply grow it */
/* grow reservation. it's probably the last one in the buffer,
but it's not guarateed because we might have used holes
as well */
do {
size -= 2;
break;
}
} while (size >= 2);
} else {
}
return 0;
}
static void
{
struct mail_cache_hole_header hole;
/* we can just set used_file_size back */
} else if (size >= MAIL_CACHE_MIN_HOLE_SIZE) {
/* set it up as a hole */
return;
}
}
}
static void
{
if (ctx->reserved_space == 0)
return;
if (!locked) {
return;
}
if (!locked)
}
static uint32_t
{
int ret;
if (!locked) {
return -1;
}
commit);
if (!locked)
if (ret < 0)
return 0;
} else {
}
if (available_space_r != NULL)
/* final commit - see if we can free the rest of the
reserved space */
}
return offset;
}
static int
{
int commit;
if (commit) {
/* committing, remove the last dummy record */
}
&max_size,
commit);
if (write_offset == 0) {
/* nothing to write / error */
}
/* see how much we can really write there */
seq_limit++;
}
} else {
}
/* write it to file */
return -1;
}
/* write the cache_offsets to index file. records' prev_offset
is updated to point to old cache record when index is being
synced. */
if (old_offset != 0) {
/* we added records for this message multiple
times in this same uncommitted transaction.
only the new one will be written to
transaction log, we need to do the linking
ourself here. */
write_offset) < 0)
return -1;
}
}
}
/* drop the written data from buffer */
return 0;
}
static void
{
void *data;
/* fix record size */
}
}
{
int i, ret = 0;
return 0;
}
if (mail_cache_lock(cache) <= 0) {
return -1;
}
if (mail_cache_transaction_flush(ctx) < 0)
ret = -1;
/* make sure everything's written before updating offsets */
ret = -1;
}
if (ret == 0) {
for (i = 0; i < MAIL_CACHE_HEADERS_COUNT; i++) {
if (offset != 0) {
}
}
}
/* they're all used - compress the cache to get more */
}
return ret;
}
{
unsigned int i;
if (size > 0) {
/* free flushed data as well. do it from end to beginning so
we have a better chance of updating used_file_size instead
of adding holes */
do {
size -= 2;
} while (size > 0);
}
/* make sure we don't cache the headers */
for (i = 0; i < ctx->next_unused_header_lowwater; i++) {
if (mail_cache_offset_to_uint32(offset) == 0)
}
}
static const char *write_header_string(const char *const headers[],
{
if (buffer_get_used_size(buffer) != 0)
headers++;
}
if ((size & 3) != 0) {
}
}
{
const char *header_str, *prev_str;
header_offsets[idx]) == 0);
t_push();
if (idx != 0) {
t_pop();
return FALSE;
}
}
if (offset != 0) {
offset = 0;
}
}
if (offset != 0) {
/* update cached headers */
/* make sure get_header_fields() still works for this header
while the transaction isn't yet committed. */
}
t_pop();
return offset > 0;
}
static size_t
enum mail_cache_field field)
{
const struct mail_cache_record *cache_rec;
const void *data;
unsigned int mask;
int i;
return pos;
if ((mask & MAIL_CACHE_FIXED_MASK) != 0)
else {
sizeof(data_size));
}
}
}
i_unreached();
return pos;
}
enum mail_cache_field field,
{
struct mail_cache_record *cache_rec;
unsigned char *buf;
unsigned int field_idx;
if ((field & MAIL_CACHE_FIXED_MASK) != 0) {
} else if ((field & MAIL_CACHE_STRING_MASK) != 0) {
}
/* remember roughly what we have modified, so cache lookups can
look into transactions to see changes. */
}
if ((field & MAIL_CACHE_FIXED_MASK) == 0)
full_size += sizeof(data_size32);
/* time to flush our buffer */
if (mail_cache_transaction_flush(ctx) < 0)
return;
}
/* fields must be ordered. find where to insert it. */
sizeof(*cache_rec));
/* @UNSAFE */
if ((field & MAIL_CACHE_FIXED_MASK) == 0) {
buf += sizeof(data_size32);
}
if ((data_size & 3) != 0)
}
enum mail_cache_record_flag flags)
{
return -1;
}
{
if (new_offset + sizeof(struct mail_cache_record) >
"Cache record offset %u points outside file",
return -1;
}
sizeof(old_offset), new_offset) < 0) {
return -1;
}
return 0;
}
{
struct mail_cache_record *cache_rec;
return 0;
/* we'll only update the deleted_space in header. we can't really
do any actual deleting as other processes might still be using
the data. also it's actually useful as some index views are still
able to ask cached data from messages that have already been
expunged. */
do {
return 0;
}