mail-cache-transaction.c revision e9f2d9104d395bcf54be3f8ba8d9f63aecf0bcbe
/* Copyright (C) 2003-2004 Timo Sirainen */
#include "lib.h"
#include "buffer.h"
#include "file-cache.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 changes:1;
};
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;
t->cache_trans_ctx = ctx;
return ctx;
}
{
if (ctx->cache_data)
ctx->reserved_space_offset = 0;
ctx->reserved_space = 0;
ctx->last_grow_size = 0;
}
{
}
{
int ret;
return ret;
return 1;
}
{
/* 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
{
}
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;
}
/* allocate some more space than we need */
}
}
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;
} else {
}
return 0;
}
static void
{
struct mail_cache_hole_header hole;
if (MAIL_CACHE_IS_UNUSABLE(cache))
return;
/* 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) {
if (mail_cache_transaction_lock(ctx) <= 0)
return;
}
/* check again - locking might have reopened the cache file */
if (ctx->reserved_space != 0) {
ctx->reserved_space_offset = 0;
ctx->reserved_space = 0;
}
if (!locked)
}
static int
int commit)
{
int ret;
/* not enough preallocated space in transaction, get more */
if (!locked) {
return ret;
}
commit);
if (!locked)
if (ret < 0)
return -1;
/* cache file reopened - need to abort */
return 0;
}
} else {
}
if (available_space_r != NULL)
/* final commit - see if we can free the rest of the
reserved space */
}
return 1;
}
static int
const struct mail_cache_record *rec,
{
/* write the cache_offsets to index file. records' prev_offset
is updated to point to old cache record when index is being
synced. */
&write_offset, &old_offset);
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. */
if (old_offset > write_offset) {
write_offset) < 0)
return -1;
} else {
/* if we're combining multiple transactions,
make sure the one with the smallest offset
is written into index. this is required for
non-file-mmaped cache to work properly. */
&old_offset, NULL);
old_offset) < 0)
return -1;
}
}
}
*seq_idx = i;
return 0;
}
static int
{
unsigned int seq_count;
if (MAIL_CACHE_IS_UNUSABLE(cache))
return -1;
if (commit) {
/* committing, remove the last dummy record */
}
/* cache file reopened - need to abort */
return 0;
}
seq_limit = 0;
if (ret <= 0) {
/* nothing to write / error / cache file reopened */
return ret;
}
/* see how much we can really write there */
seq_limit++;
}
} else {
}
/* write it to file */
return -1;
}
&write_size) < 0)
return -1;
rec_pos += write_size;
}
/* drop the written data from buffer */
return 0;
}
static void
{
void *data;
/* fix record size */
}
}
{
int ret = 0;
return 0;
}
if (mail_cache_transaction_lock(ctx) <= 0) {
return -1;
}
if (mail_cache_transaction_flush(ctx) < 0)
ret = -1;
/* make sure everything's written before updating offsets */
ret = -1;
}
return ret;
}
{
if (mail_cache_transaction_lock(ctx) > 0) {
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);
}
}
}
}
unsigned int field)
{
const void *data;
int ret = 0;
if (mail_cache_transaction_lock(ctx) <= 0)
return -1;
/* re-read header to make sure we don't lose any fields. */
if (mail_cache_header_fields_read(cache) < 0) {
return -1;
}
/* it was already added */
return 0;
}
t_push();
ret = -1;
ret = -1;
ret = -1;
} else if (mail_cache_header_fields_get_next_offset(cache,
&hdr_offset) < 0)
ret = -1;
else {
/* after it's guaranteed to be in disk, update header offset */
hdr_offset) < 0) {
ret = -1;
} else {
/* we'll need to fix mappings. */
if (mail_cache_header_fields_read(cache) < 0)
ret = -1;
}
}
t_pop();
return ret;
}
{
unsigned int fixed_size;
return;
/* we'll have to add this field to headers */
return;
}
/* remember roughly what we have modified, so cache lookups can
look into transactions to see changes. */
}
if (fixed_size == (unsigned int)-1)
full_size += sizeof(data_size32);
/* time to flush our buffer */
if (mail_cache_transaction_flush(ctx) < 0)
return;
}
if (fixed_size == (unsigned int)-1) {
sizeof(data_size32));
}
if ((data_size & 3) != 0)
}
{
sizeof(old_offset), new_offset) < 0) {
return -1;
}
return 0;
}
{
if (MAIL_CACHE_IS_UNUSABLE(cache))
return -1;
if (new_offset + sizeof(struct mail_cache_record) >
"Cache record offset %u points outside file",
return -1;
}
return -1;
return 0;
}
{
const struct mail_cache_record *cache_rec;
return -1;
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 {
&cache_rec) < 0)
return -1;
return 0;
}