mail-cache-transaction.c revision e9f2d9104d395bcf54be3f8ba8d9f63aecf0bcbe
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi/* Copyright (C) 2003-2004 Timo Sirainen */
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi#include "lib.h"
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi#include "buffer.h"
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi#include "file-cache.h"
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi#include "file-set-size.h"
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi#include "read-full.h"
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi#include "write-full.h"
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi#include "mail-cache-private.h"
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi#include "mail-index-transaction-private.h"
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi#include <stddef.h>
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi#include <sys/stat.h>
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumistruct mail_cache_transaction_ctx {
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi struct mail_cache *cache;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi struct mail_cache_view *view;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi struct mail_index_transaction *trans;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi uint32_t cache_file_seq;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi buffer_t *cache_data;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi array_t ARRAY_DEFINE(cache_data_seq, uint32_t);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi uint32_t prev_seq;
8900b9eb2514c07047541833286428572493a9fdStéphane Graber size_t prev_pos;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi buffer_t *reservations;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi uint32_t reserved_space_offset, reserved_space;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi uint32_t last_grow_size;
faefa7f8584a7d1567df2e6f1f9240a28a6466abStéphane Graber
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi unsigned int changes:1;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi};
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumistatic int mail_cache_link_unlocked(struct mail_cache *cache,
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi uint32_t old_offset, uint32_t new_offset);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumistruct mail_cache_transaction_ctx *
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumimail_cache_get_transaction(struct mail_cache_view *view,
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi struct mail_index_transaction *t)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi{
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi struct mail_cache_transaction_ctx *ctx;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (t->cache_trans_ctx != NULL)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi return t->cache_trans_ctx;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi ctx = i_new(struct mail_cache_transaction_ctx, 1);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi ctx->cache = view->cache;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi ctx->view = view;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi ctx->trans = t;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi ctx->reservations = buffer_create_dynamic(system_pool, 256);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
6127da6b3f5815028bee187ac98840cd94313841KATOH Yasufumi if (!MAIL_CACHE_IS_UNUSABLE(ctx->cache))
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi ctx->cache_file_seq = ctx->cache->hdr->file_seq;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi i_assert(view->transaction == NULL);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi view->transaction = ctx;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi view->trans_view = mail_index_transaction_open_updated_view(t);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi t->cache_trans_ctx = ctx;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi return ctx;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi}
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumistatic void mail_cache_transaction_reset(struct mail_cache_transaction_ctx *ctx)
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi{
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi ctx->cache_file_seq = ctx->cache->hdr->file_seq;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (ctx->cache_data)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi buffer_set_used_size(ctx->cache_data, 0);
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi if (array_is_created(&ctx->cache_data_seq))
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi array_clear(&ctx->cache_data_seq);
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi ctx->prev_seq = 0;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi ctx->prev_pos = 0;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi buffer_set_used_size(ctx->reservations, 0);
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi ctx->reserved_space_offset = 0;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi ctx->reserved_space = 0;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi ctx->last_grow_size = 0;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi ctx->changes = FALSE;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi}
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumistatic void mail_cache_transaction_free(struct mail_cache_transaction_ctx *ctx)
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi{
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi ctx->view->transaction = NULL;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi ctx->view->trans_seq1 = ctx->view->trans_seq2 = 0;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (ctx->cache_data != NULL)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi buffer_free(ctx->cache_data);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (array_is_created(&ctx->cache_data_seq))
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi array_free(&ctx->cache_data_seq);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi buffer_free(ctx->reservations);
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi i_free(ctx);
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi}
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumistatic int mail_cache_transaction_lock(struct mail_cache_transaction_ctx *ctx)
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi{
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi int ret;
48e49f08c0f0d33d75e42cd1f6bf446f740fff8aKATOH Yasufumi
99282c429a23a2ffa699ca149bb7f9cd5705646aKATOH Yasufumi if ((ret = mail_cache_lock(ctx->cache)) <= 0)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi return ret;
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi if (ctx->cache_file_seq != ctx->cache->hdr->file_seq)
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi mail_cache_transaction_reset(ctx);
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi return 1;
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi}
48e49f08c0f0d33d75e42cd1f6bf446f740fff8aKATOH Yasufumi
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumistatic int mail_cache_grow_file(struct mail_cache *cache, size_t size)
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi{
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi struct stat st;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi uoff_t new_fsize, grow_size;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi i_assert(cache->locked);
e8ea311657f82b91710dfcb8bca656bd5b94c66cKATOH Yasufumi
e8ea311657f82b91710dfcb8bca656bd5b94c66cKATOH Yasufumi /* grow the file */
e8ea311657f82b91710dfcb8bca656bd5b94c66cKATOH Yasufumi new_fsize = cache->hdr_copy.used_file_size + size;
e8ea311657f82b91710dfcb8bca656bd5b94c66cKATOH Yasufumi grow_size = new_fsize / 100 * MAIL_CACHE_GROW_PERCENTAGE;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi if (grow_size < 16384)
e8ea311657f82b91710dfcb8bca656bd5b94c66cKATOH Yasufumi grow_size = 16384;
e8ea311657f82b91710dfcb8bca656bd5b94c66cKATOH Yasufumi new_fsize += grow_size;
48e49f08c0f0d33d75e42cd1f6bf446f740fff8aKATOH Yasufumi new_fsize &= ~1023;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi if (fstat(cache->fd, &st) < 0) {
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi mail_cache_set_syscall_error(cache, "fstat()");
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi return -1;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi }
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi if ((uoff_t)st.st_size < new_fsize) {
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi if (file_set_size(cache->fd, new_fsize) < 0) {
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi mail_cache_set_syscall_error(cache, "file_set_size()");
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi return -1;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi }
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi }
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi return 0;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi}
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumistatic int mail_cache_unlink_hole(struct mail_cache *cache, size_t size,
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi struct mail_cache_hole_header *hole_r)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi{
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi struct mail_cache_header *hdr = &cache->hdr_copy;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi struct mail_cache_hole_header hole;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi uint32_t offset, prev_offset;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi i_assert(cache->locked);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi offset = hdr->hole_offset; prev_offset = 0;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi while (offset != 0) {
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (pread_full(cache->fd, &hole, sizeof(hole), offset) <= 0) {
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi mail_cache_set_syscall_error(cache, "pread_full()");
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi return FALSE;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi }
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (hole.magic != MAIL_CACHE_HOLE_HEADER_MAGIC) {
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi mail_cache_set_corrupted(cache,
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi "Invalid magic in hole header");
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi return FALSE;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi }
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (hole.size >= size)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi break;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi prev_offset = offset;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi offset = hole.next_offset;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi }
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (offset == 0)
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi return FALSE;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (prev_offset == 0)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi hdr->hole_offset = hole.next_offset;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi else {
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (pwrite_full(cache->fd, &hole.next_offset,
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi sizeof(hole.next_offset), prev_offset) < 0) {
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi mail_cache_set_syscall_error(cache, "pwrite_full()");
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi return FALSE;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi }
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi }
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi hdr->deleted_space -= hole.size;
99282c429a23a2ffa699ca149bb7f9cd5705646aKATOH Yasufumi cache->hdr_modified = TRUE;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi hole_r->next_offset = offset;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi hole_r->size = hole.size;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi return TRUE;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi}
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumistatic void
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumimail_cache_transaction_add_reservation(struct mail_cache_transaction_ctx *ctx,
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi uint32_t offset, uint32_t size)
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi{
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi ctx->reserved_space_offset = offset;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi ctx->reserved_space = size;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi buffer_append(ctx->reservations, &offset, sizeof(offset));
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi buffer_append(ctx->reservations, &size, sizeof(size));
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi}
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumistatic int
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumimail_cache_transaction_reserve_more(struct mail_cache_transaction_ctx *ctx,
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi size_t block_size, int commit)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi{
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi struct mail_cache *cache = ctx->cache;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi struct mail_cache_header *hdr = &cache->hdr_copy;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi struct mail_cache_hole_header hole;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi uint32_t *buf;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi size_t size;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi i_assert(cache->locked);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (mail_cache_unlink_hole(cache, block_size, &hole)) {
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi /* found a large enough hole. */
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi mail_cache_transaction_add_reservation(ctx, hole.next_offset,
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi hole.size);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi return 0;
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi }
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi if (MAIL_CACHE_IS_UNUSABLE(cache)) {
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi /* mail_cache_unlink_hole() could have noticed corruption */
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi return -1;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi }
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi if ((uint32_t)-1 - hdr->used_file_size < block_size) {
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi mail_index_set_error(cache->index, "Cache file too large: %s",
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi cache->filepath);
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi return -1;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi }
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi if (!commit && block_size < MAIL_CACHE_MAX_RESERVED_BLOCK_SIZE) {
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi /* allocate some more space than we need */
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi size_t new_block_size = (block_size + ctx->last_grow_size) * 2;
f7f1ba77b76e4d4dc18638cfdc859c3dc1750a9eStéphane Graber if (new_block_size > MAIL_CACHE_MAX_RESERVED_BLOCK_SIZE)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi new_block_size = MAIL_CACHE_MAX_RESERVED_BLOCK_SIZE;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if ((uint32_t)-1 - hdr->used_file_size >= new_block_size) {
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi block_size = new_block_size;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi ctx->last_grow_size = new_block_size;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi }
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi }
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (mail_cache_grow_file(ctx->cache, block_size) < 0)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi return -1;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi if (ctx->reserved_space_offset + ctx->reserved_space ==
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi hdr->used_file_size) {
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi /* we can simply grow it */
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi /* grow reservation. it's probably the last one in the buffer,
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi but it's not guarateed because we might have used holes
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi as well */
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi buf = buffer_get_modifyable_data(ctx->reservations, &size);
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi size /= sizeof(uint32_t);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi do {
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi i_assert(size >= 2);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi size -= 2;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi } while (buf[size] + buf[size+1] != hdr->used_file_size);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi buf[size+1] += block_size;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi ctx->reserved_space += block_size;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi } else {
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi mail_cache_transaction_add_reservation(ctx, hdr->used_file_size,
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi block_size);
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi }
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi cache->hdr_modified = TRUE;
48e49f08c0f0d33d75e42cd1f6bf446f740fff8aKATOH Yasufumi hdr->used_file_size = ctx->reserved_space_offset + ctx->reserved_space;
48e49f08c0f0d33d75e42cd1f6bf446f740fff8aKATOH Yasufumi return 0;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi}
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumistatic void
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumimail_cache_free_space(struct mail_cache *cache, uint32_t offset, uint32_t size)
48e49f08c0f0d33d75e42cd1f6bf446f740fff8aKATOH Yasufumi{
48e49f08c0f0d33d75e42cd1f6bf446f740fff8aKATOH Yasufumi struct mail_cache_hole_header hole;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi i_assert(cache->locked);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (MAIL_CACHE_IS_UNUSABLE(cache))
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi return;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi if (offset + size == cache->hdr_copy.used_file_size) {
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi /* we can just set used_file_size back */
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi cache->hdr_modified = TRUE;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi cache->hdr_copy.used_file_size = offset;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi } else if (size >= MAIL_CACHE_MIN_HOLE_SIZE) {
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi /* set it up as a hole */
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi hole.next_offset = cache->hdr_copy.hole_offset;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi hole.size = size;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi hole.magic = MAIL_CACHE_HOLE_HEADER_MAGIC;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (pwrite_full(cache->fd, &hole, sizeof(hole), offset) < 0) {
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi mail_cache_set_syscall_error(cache, "pwrite_full()");
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi return;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi }
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi cache->hdr_copy.deleted_space += size;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi cache->hdr_copy.hole_offset = offset;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi cache->hdr_modified = TRUE;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi }
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi}
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumistatic void
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumimail_cache_transaction_free_space(struct mail_cache_transaction_ctx *ctx)
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi{
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi int locked = ctx->cache->locked;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi if (ctx->reserved_space == 0)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi return;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (!locked) {
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (mail_cache_transaction_lock(ctx) <= 0)
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi return;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi }
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi /* check again - locking might have reopened the cache file */
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi if (ctx->reserved_space != 0) {
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi i_assert(ctx->cache_file_seq == ctx->cache->hdr->file_seq);
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi mail_cache_free_space(ctx->cache, ctx->reserved_space_offset,
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi ctx->reserved_space);
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi ctx->reserved_space_offset = 0;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi ctx->reserved_space = 0;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi }
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi if (!locked)
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi mail_cache_unlock(ctx->cache);
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi}
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumistatic int
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumimail_cache_transaction_get_space(struct mail_cache_transaction_ctx *ctx,
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi size_t min_size, size_t max_size,
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi uint32_t *offset_r, size_t *available_space_r,
9a97d4e4bdf331bb2c2b8ed14bcefa53358c288fKATOH Yasufumi int commit)
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi{
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi int locked = ctx->cache->locked;
a5ba96715d4ef264c43d4f187251de491ba198c0KATOH Yasufumi uint32_t cache_file_seq;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi size_t size;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi int ret;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi i_assert((min_size & 3) == 0);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi i_assert((max_size & 3) == 0);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (min_size > ctx->reserved_space) {
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi /* not enough preallocated space in transaction, get more */
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi cache_file_seq = ctx->cache_file_seq;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (!locked) {
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if ((ret = mail_cache_transaction_lock(ctx)) <= 0)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi return ret;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi }
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi ret = mail_cache_transaction_reserve_more(ctx, max_size,
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi commit);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (!locked)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi mail_cache_unlock(ctx->cache);
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (ret < 0)
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi return -1;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi if (cache_file_seq != ctx->cache_file_seq) {
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi /* cache file reopened - need to abort */
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi return 0;
57da8c32f85c0255efa61ee32e260068afdaa565KATOH Yasufumi }
size = max_size;
} else {
size = I_MIN(max_size, ctx->reserved_space);
}
*offset_r = ctx->reserved_space_offset;
ctx->reserved_space_offset += size;
ctx->reserved_space -= size;
if (available_space_r != NULL)
*available_space_r = size;
i_assert((size & 3) == 0);
if (size == max_size && commit) {
/* final commit - see if we can free the rest of the
reserved space */
mail_cache_transaction_free_space(ctx);
}
i_assert(size >= min_size);
return 1;
}
static int
mail_cache_transaction_update_index(struct mail_cache_transaction_ctx *ctx,
const struct mail_cache_record *rec,
const uint32_t *seq, uint32_t *seq_idx,
uint32_t seq_limit, uint32_t write_offset,
uint32_t *size_r)
{
struct mail_cache *cache = ctx->cache;
uint32_t i, old_offset, orig_write_offset;
/* write the cache_offsets to index file. records' prev_offset
is updated to point to old cache record when index is being
synced. */
orig_write_offset = write_offset;
for (i = *seq_idx; i < seq_limit; i++) {
mail_index_update_ext(ctx->trans, seq[i], cache->ext_id,
&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) {
if (mail_cache_link_unlocked(cache, old_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. */
mail_index_update_ext(ctx->trans, seq[i],
cache->ext_id,
&old_offset, NULL);
if (mail_cache_link_unlocked(cache,
write_offset,
old_offset) < 0)
return -1;
}
}
write_offset += rec->size;
rec = CONST_PTR_OFFSET(rec, rec->size);
}
*seq_idx = i;
*size_r = write_offset - orig_write_offset;
return 0;
}
static int
mail_cache_transaction_flush(struct mail_cache_transaction_ctx *ctx)
{
struct mail_cache *cache = ctx->cache;
const struct mail_cache_record *rec, *tmp_rec;
const uint32_t *seq;
uint32_t write_offset, write_size, rec_pos, seq_idx, seq_limit;
size_t size, max_size;
unsigned int seq_count;
int ret, commit;
if (MAIL_CACHE_IS_UNUSABLE(cache))
return -1;
commit = ctx->prev_seq == 0;
if (commit) {
/* committing, remove the last dummy record */
buffer_set_used_size(ctx->cache_data, ctx->prev_pos);
}
if (ctx->cache_file_seq != ctx->cache->hdr->file_seq) {
/* cache file reopened - need to abort */
mail_cache_transaction_reset(ctx);
return 0;
}
rec = buffer_get_data(ctx->cache_data, &size);
i_assert(ctx->prev_pos <= size);
seq = array_get(&ctx->cache_data_seq, &seq_count);
seq_limit = 0;
for (seq_idx = 0, rec_pos = 0; rec_pos < ctx->prev_pos;) {
max_size = ctx->prev_pos - rec_pos;
ret = mail_cache_transaction_get_space(ctx, rec->size,
max_size, &write_offset,
&max_size, commit);
if (ret <= 0) {
/* nothing to write / error / cache file reopened */
return ret;
}
if (rec_pos + max_size < ctx->prev_pos) {
/* see how much we can really write there */
tmp_rec = rec;
for (size = 0; size + tmp_rec->size <= max_size; ) {
seq_limit++;
size += tmp_rec->size;
tmp_rec = CONST_PTR_OFFSET(tmp_rec,
tmp_rec->size);
}
max_size = size;
} else {
seq_limit = seq_count;
}
/* write it to file */
i_assert(ctx->cache_file_seq == cache->hdr->file_seq);
if (pwrite_full(cache->fd, rec, max_size, write_offset) < 0) {
mail_cache_set_syscall_error(cache, "pwrite_full()");
return -1;
}
if (mail_cache_transaction_update_index(ctx, rec, seq,
&seq_idx, seq_limit,
write_offset,
&write_size) < 0)
return -1;
rec_pos += write_size;
rec = CONST_PTR_OFFSET(rec, write_size);
}
/* drop the written data from buffer */
buffer_copy(ctx->cache_data, 0,
ctx->cache_data, ctx->prev_pos, (size_t)-1);
buffer_set_used_size(ctx->cache_data,
buffer_get_used_size(ctx->cache_data) -
ctx->prev_pos);
ctx->prev_pos = 0;
array_clear(&ctx->cache_data_seq);
return 0;
}
static void
mail_cache_transaction_switch_seq(struct mail_cache_transaction_ctx *ctx)
{
struct mail_cache_record *rec, new_rec;
void *data;
size_t size;
if (ctx->prev_seq != 0) {
/* fix record size */
data = buffer_get_modifyable_data(ctx->cache_data, &size);
rec = PTR_OFFSET(data, ctx->prev_pos);
rec->size = size - ctx->prev_pos;
i_assert(rec->size != 0);
array_append(&ctx->cache_data_seq, &ctx->prev_seq, 1);
ctx->prev_pos = size;
} else if (ctx->cache_data == NULL) {
ctx->cache_data = buffer_create_dynamic(default_pool, 32768);
ARRAY_CREATE(&ctx->cache_data_seq, default_pool, uint32_t, 64);
}
memset(&new_rec, 0, sizeof(new_rec));
buffer_append(ctx->cache_data, &new_rec, sizeof(new_rec));
ctx->prev_seq = 0;
ctx->changes = TRUE;
}
int mail_cache_transaction_commit(struct mail_cache_transaction_ctx *ctx)
{
struct mail_cache *cache = ctx->cache;
int ret = 0;
if (!ctx->changes || MAIL_CACHE_IS_UNUSABLE(cache)) {
mail_cache_transaction_free(ctx);
return 0;
}
if (mail_cache_transaction_lock(ctx) <= 0) {
mail_cache_transaction_rollback(ctx);
return -1;
}
if (ctx->prev_seq != 0)
mail_cache_transaction_switch_seq(ctx);
if (mail_cache_transaction_flush(ctx) < 0)
ret = -1;
/* make sure everything's written before updating offsets */
if (fdatasync(cache->fd) < 0) {
mail_cache_set_syscall_error(cache, "fdatasync()");
ret = -1;
}
mail_cache_unlock(cache);
mail_cache_transaction_free(ctx);
return ret;
}
void mail_cache_transaction_rollback(struct mail_cache_transaction_ctx *ctx)
{
struct mail_cache *cache = ctx->cache;
const uint32_t *buf;
size_t size;
if ((ctx->reserved_space > 0 || ctx->reservations->used > 0) &&
!MAIL_CACHE_IS_UNUSABLE(cache)) {
if (mail_cache_transaction_lock(ctx) > 0) {
buf = buffer_get_data(ctx->reservations, &size);
i_assert(size % sizeof(uint32_t)*2 == 0);
size /= sizeof(*buf);
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;
mail_cache_free_space(ctx->cache,
buf[size],
buf[size+1]);
} while (size > 0);
}
mail_cache_unlock(cache);
}
}
mail_cache_transaction_free(ctx);
}
static int mail_cache_header_add_field(struct mail_cache_transaction_ctx *ctx,
unsigned int field)
{
struct mail_cache *cache = ctx->cache;
buffer_t *buffer;
const void *data;
size_t size;
uint32_t offset, hdr_offset;
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) {
mail_cache_unlock(cache);
return -1;
}
if (ctx->cache->field_file_map[field] != (uint32_t)-1) {
/* it was already added */
mail_cache_unlock(cache);
return 0;
}
t_push();
buffer = buffer_create_dynamic(pool_datastack_create(), 256);
mail_cache_header_fields_get(cache, buffer);
data = buffer_get_data(buffer, &size);
if (mail_cache_transaction_get_space(ctx, size, size,
&offset, &size, TRUE) <= 0)
ret = -1;
else if (pwrite_full(cache->fd, data, size, offset) < 0) {
mail_cache_set_syscall_error(cache, "pwrite_full()");
ret = -1;
} else if (fdatasync(cache->fd) < 0) {
mail_cache_set_syscall_error(cache, "fdatasync()");
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 */
offset = mail_index_uint32_to_offset(offset);
if (pwrite_full(cache->fd, &offset, sizeof(offset),
hdr_offset) < 0) {
mail_cache_set_syscall_error(cache, "pwrite_full()");
ret = -1;
} else {
/* we'll need to fix mappings. */
if (mail_cache_header_fields_read(cache) < 0)
ret = -1;
}
}
t_pop();
mail_cache_unlock(cache);
return ret;
}
void mail_cache_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq,
unsigned int field, const void *data, size_t data_size)
{
uint32_t file_field, data_size32;
unsigned int fixed_size;
size_t full_size;
i_assert(field < ctx->cache->fields_count);
i_assert(data_size < (uint32_t)-1);
if (ctx->cache->fields[field].field.decision ==
(MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED))
return;
file_field = ctx->cache->field_file_map[field];
if (file_field == (uint32_t)-1) {
/* we'll have to add this field to headers */
if (mail_cache_header_add_field(ctx, field) < 0)
return;
file_field = ctx->cache->field_file_map[field];
i_assert(file_field != (uint32_t)-1);
}
mail_cache_decision_add(ctx->view, seq, field);
fixed_size = ctx->cache->fields[field].field.field_size;
i_assert(fixed_size == (unsigned int)-1 || fixed_size == data_size);
data_size32 = (uint32_t)data_size;
if (ctx->prev_seq != seq) {
mail_cache_transaction_switch_seq(ctx);
ctx->prev_seq = seq;
/* remember roughly what we have modified, so cache lookups can
look into transactions to see changes. */
if (seq < ctx->view->trans_seq1 || ctx->view->trans_seq1 == 0)
ctx->view->trans_seq1 = seq;
if (seq > ctx->view->trans_seq2)
ctx->view->trans_seq2 = seq;
}
full_size = (data_size + 3) & ~3;
if (fixed_size == (unsigned int)-1)
full_size += sizeof(data_size32);
if (buffer_get_used_size(ctx->cache_data) + full_size >
buffer_get_size(ctx->cache_data)) {
/* time to flush our buffer */
if (mail_cache_transaction_flush(ctx) < 0)
return;
}
buffer_append(ctx->cache_data, &file_field, sizeof(file_field));
if (fixed_size == (unsigned int)-1) {
buffer_append(ctx->cache_data, &data_size32,
sizeof(data_size32));
}
buffer_append(ctx->cache_data, data, data_size);
if ((data_size & 3) != 0)
buffer_append(ctx->cache_data, null4, 4 - (data_size & 3));
}
static int mail_cache_link_unlocked(struct mail_cache *cache,
uint32_t old_offset, uint32_t new_offset)
{
new_offset += offsetof(struct mail_cache_record, prev_offset);
if (pwrite_full(cache->fd, &old_offset,
sizeof(old_offset), new_offset) < 0) {
mail_cache_set_syscall_error(cache, "pwrite_full()");
return -1;
}
return 0;
}
int mail_cache_link(struct mail_cache *cache, uint32_t old_offset,
uint32_t new_offset)
{
i_assert(cache->locked);
if (MAIL_CACHE_IS_UNUSABLE(cache))
return -1;
if (new_offset + sizeof(struct mail_cache_record) >
cache->hdr_copy.used_file_size) {
mail_cache_set_corrupted(cache,
"Cache record offset %u points outside file",
new_offset);
return -1;
}
if (mail_cache_link_unlocked(cache, old_offset, new_offset) < 0)
return -1;
cache->hdr_copy.continued_record_count++;
cache->hdr_modified = TRUE;
return 0;
}
int mail_cache_delete(struct mail_cache *cache, uint32_t offset)
{
const struct mail_cache_record *cache_rec;
i_assert(cache->locked);
if (mail_cache_get_record(cache, offset, &cache_rec) < 0)
return -1;
if (cache_rec == NULL)
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->hdr_copy.deleted_space += cache_rec->size;
if (mail_cache_get_record(cache, cache_rec->prev_offset,
&cache_rec) < 0)
return -1;
} while (cache_rec != NULL);
cache->hdr_modified = TRUE;
return 0;
}