mail-cache-transaction.c revision cfbf891f065b18602703ed6fa2af1a541d4d0b04
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi/* Copyright (c) 2003-2007 Dovecot authors, see the included COPYING file */
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi#include "lib.h"
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi#include "ioloop.h"
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi#include "array.h"
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi#include "buffer.h"
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi#include "file-cache.h"
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi#include "file-set-size.h"
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi#include "read-full.h"
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi#include "write-full.h"
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi#include "mail-cache-private.h"
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi#include "mail-index-transaction-private.h"
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi#include <stddef.h>
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi#include <sys/stat.h>
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi#define MAIL_CACHE_WRITE_BUFFER 32768
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumistruct mail_cache_reservation {
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi uint32_t offset;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi uint32_t size;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi};
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumistruct mail_cache_transaction_ctx {
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi struct mail_cache *cache;
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi struct mail_cache_view *view;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi struct mail_index_transaction *trans;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi uint32_t cache_file_seq;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi buffer_t *cache_data;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ARRAY_DEFINE(cache_data_seq, uint32_t);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi uint32_t prev_seq;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi size_t prev_pos;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ARRAY_DEFINE(reservations, struct mail_cache_reservation);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi uint32_t reserved_space_offset, reserved_space;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi uint32_t last_grow_size;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi unsigned int tried_compression:1;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi unsigned int changes:1;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi};
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumistatic int mail_cache_link_unlocked(struct mail_cache *cache,
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi uint32_t old_offset, uint32_t new_offset);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumistruct mail_cache_transaction_ctx *
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumimail_cache_get_transaction(struct mail_cache_view *view,
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi struct mail_index_transaction *t)
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi{
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi struct mail_cache_transaction_ctx *ctx;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (t->cache_trans_ctx != NULL)
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi return t->cache_trans_ctx;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ctx = i_new(struct mail_cache_transaction_ctx, 1);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ctx->cache = view->cache;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ctx->view = view;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ctx->trans = t;
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumi i_array_init(&ctx->reservations, 32);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi i_assert(view->transaction == NULL);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi view->transaction = ctx;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi view->trans_view = mail_index_transaction_open_updated_view(t);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi t->cache_trans_ctx = ctx;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi return ctx;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi}
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumistatic void mail_cache_transaction_reset(struct mail_cache_transaction_ctx *ctx)
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi{
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ctx->cache_file_seq = MAIL_CACHE_IS_UNUSABLE(ctx->cache) ? 0 :
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ctx->cache->hdr->file_seq;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi mail_index_ext_set_reset_id(ctx->trans, ctx->cache->ext_id,
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ctx->cache_file_seq);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (ctx->cache_data != NULL)
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi buffer_set_used_size(ctx->cache_data, 0);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (array_is_created(&ctx->cache_data_seq))
dc421f3aac1f0e516c763dd156629a8ed2a7b4caKATOH Yasufumi array_clear(&ctx->cache_data_seq);
dc421f3aac1f0e516c763dd156629a8ed2a7b4caKATOH Yasufumi ctx->prev_seq = 0;
dc421f3aac1f0e516c763dd156629a8ed2a7b4caKATOH Yasufumi ctx->prev_pos = 0;
dc421f3aac1f0e516c763dd156629a8ed2a7b4caKATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi array_clear(&ctx->reservations);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ctx->reserved_space_offset = 0;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ctx->reserved_space = 0;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ctx->last_grow_size = 0;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ctx->changes = FALSE;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi}
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumistatic void
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumimail_cache_transaction_free(struct mail_cache_transaction_ctx **_ctx)
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi{
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi struct mail_cache_transaction_ctx *ctx = *_ctx;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi *_ctx = NULL;
dc421f3aac1f0e516c763dd156629a8ed2a7b4caKATOH Yasufumi
dc421f3aac1f0e516c763dd156629a8ed2a7b4caKATOH Yasufumi ctx->trans->cache_trans_ctx = NULL;
dc421f3aac1f0e516c763dd156629a8ed2a7b4caKATOH Yasufumi ctx->view->transaction = NULL;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ctx->view->trans_seq1 = ctx->view->trans_seq2 = 0;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi mail_index_view_close(&ctx->view->trans_view);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (ctx->cache_data != NULL)
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi buffer_free(&ctx->cache_data);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (array_is_created(&ctx->cache_data_seq))
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi array_free(&ctx->cache_data_seq);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi array_free(&ctx->reservations);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi i_free(ctx);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi}
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumistatic int mail_cache_transaction_lock(struct mail_cache_transaction_ctx *ctx)
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi{
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi int ret;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
dc421f3aac1f0e516c763dd156629a8ed2a7b4caKATOH Yasufumi if (ctx->cache_file_seq == 0) {
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (!ctx->cache->opened)
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi (void)mail_cache_open_and_verify(ctx->cache);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (!MAIL_CACHE_IS_UNUSABLE(ctx->cache)) {
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi i_assert(ctx->cache_data == NULL ||
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ctx->cache_data->used == 0);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi ctx->cache_file_seq = ctx->cache->hdr->file_seq;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi }
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi }
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if ((ret = mail_cache_lock(ctx->cache, FALSE)) <= 0)
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi return ret;
5fd9f759c1575525ef5f8d31b6c4a2ae89e0e12fKATOH Yasufumi
5fd9f759c1575525ef5f8d31b6c4a2ae89e0e12fKATOH Yasufumi if (ctx->cache_file_seq != ctx->cache->hdr->file_seq)
5fd9f759c1575525ef5f8d31b6c4a2ae89e0e12fKATOH Yasufumi mail_cache_transaction_reset(ctx);
5fd9f759c1575525ef5f8d31b6c4a2ae89e0e12fKATOH Yasufumi return 1;
5fd9f759c1575525ef5f8d31b6c4a2ae89e0e12fKATOH Yasufumi}
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
dc421f3aac1f0e516c763dd156629a8ed2a7b4caKATOH Yasufumistatic int mail_cache_grow_file(struct mail_cache *cache, size_t size)
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi{
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi struct stat st;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi uoff_t new_fsize, grow_size;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi i_assert(cache->locked);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi /* grow the file */
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi new_fsize = cache->hdr_copy.used_file_size + size;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi grow_size = new_fsize / 100 * MAIL_CACHE_GROW_PERCENTAGE;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (grow_size < 16384)
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi grow_size = 16384;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi new_fsize += grow_size;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi new_fsize &= ~1023;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
dc421f3aac1f0e516c763dd156629a8ed2a7b4caKATOH Yasufumi if (fstat(cache->fd, &st) < 0) {
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi mail_cache_set_syscall_error(cache, "fstat()");
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi return -1;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi }
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if ((uoff_t)st.st_size < new_fsize) {
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (file_set_size(cache->fd, new_fsize) < 0) {
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi mail_cache_set_syscall_error(cache, "file_set_size()");
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi return -1;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi }
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi }
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi return 0;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi}
3ae656b54b1688d1eb8332f80f356773e762cc99KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumistatic bool mail_cache_unlink_hole(struct mail_cache *cache, size_t size,
3ae656b54b1688d1eb8332f80f356773e762cc99KATOH Yasufumi struct mail_cache_hole_header *hole_r)
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi{
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi struct mail_cache_header *hdr = &cache->hdr_copy;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi struct mail_cache_hole_header hole;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi uint32_t offset, prev_offset;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi i_assert(cache->locked);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi offset = hdr->hole_offset; prev_offset = 0;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi while (offset != 0) {
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (pread_full(cache->fd, &hole, sizeof(hole), offset) <= 0) {
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi mail_cache_set_syscall_error(cache, "pread_full()");
2da1f56ce304ad8dbd651d1305de9b6d58dd19dbKATOH Yasufumi return FALSE;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi }
dc421f3aac1f0e516c763dd156629a8ed2a7b4caKATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (hole.magic != MAIL_CACHE_HOLE_HEADER_MAGIC) {
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi mail_cache_set_corrupted(cache,
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi "Invalid magic in hole header");
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi return FALSE;
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi }
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi if (hole.size >= size)
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi break;
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi prev_offset = offset;
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi offset = hole.next_offset;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi }
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi if (offset == 0)
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi return FALSE;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi if (prev_offset == 0)
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi hdr->hole_offset = hole.next_offset;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi else {
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi if (mail_cache_write(cache, &hole.next_offset,
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi sizeof(hole.next_offset), prev_offset) < 0)
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi return FALSE;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi }
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi hdr->deleted_space -= hole.size;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi cache->hdr_modified = TRUE;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi hole_r->next_offset = offset;
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi hole_r->size = hole.size;
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi return TRUE;
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi}
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumistatic void
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumimail_cache_transaction_add_reservation(struct mail_cache_transaction_ctx *ctx,
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi uint32_t offset, uint32_t size)
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi{
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi struct mail_cache_reservation res;
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi ctx->reserved_space_offset = offset;
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi ctx->reserved_space = size;
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi
dc421f3aac1f0e516c763dd156629a8ed2a7b4caKATOH Yasufumi res.offset = offset;
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi res.size = size;
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi
9111aa793259eb1de897af6add5bf0400e962f9fKATOH Yasufumi array_append(&ctx->reservations, &res, 1);
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumi}
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumi
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumistatic void
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumimail_cache_transaction_partial_commit(struct mail_cache_transaction_ctx *ctx,
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumi uint32_t offset, uint32_t size)
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumi{
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumi struct mail_cache_reservation *res;
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumi unsigned int i, count;
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumi
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumi if (offset + size == ctx->cache->hdr_copy.used_file_size &&
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumi offset + size == ctx->reserved_space_offset) {
dc421f3aac1f0e516c763dd156629a8ed2a7b4caKATOH Yasufumi i_assert(ctx->reserved_space == 0);
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumi ctx->reserved_space_offset = 0;
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumi }
800648d24ad2791bc656e47f3a048c01fbe27587KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi res = array_get_modifiable(&ctx->reservations, &count);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi for (i = 0; i < count; i++) {
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (res[i].offset == offset) {
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi if (res[i].size == size) {
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi array_delete(&ctx->reservations, i, 1);
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi } else {
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi i_assert(res[i].size > size);
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi res[i].offset += size;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi res[i].size -= size;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi }
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi break;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi }
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi }
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi}
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumistatic int
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumimail_cache_transaction_reserve_more(struct mail_cache_transaction_ctx *ctx,
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi size_t block_size, bool commit)
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi{
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi struct mail_cache *cache = ctx->cache;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi struct mail_cache_header *hdr = &cache->hdr_copy;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi struct mail_cache_hole_header hole;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi struct mail_cache_reservation *reservations;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi unsigned int count;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi i_assert(cache->locked);
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi if (mail_cache_unlink_hole(cache, block_size, &hole)) {
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi /* found a large enough hole. */
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi mail_cache_transaction_add_reservation(ctx, hole.next_offset,
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi hole.size);
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi return 0;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi }
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi if (MAIL_CACHE_IS_UNUSABLE(cache)) {
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi /* mail_cache_unlink_hole() could have noticed corruption */
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi return -1;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi }
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi if ((uint32_t)-1 - hdr->used_file_size < block_size) {
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi mail_index_set_error(cache->index, "Cache file too large: %s",
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi cache->filepath);
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi return -1;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi }
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi if (!commit && block_size < MAIL_CACHE_MAX_RESERVED_BLOCK_SIZE) {
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi /* allocate some more space than we need */
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi size_t new_block_size = (block_size + ctx->last_grow_size) * 2;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi if (new_block_size > MAIL_CACHE_MAX_RESERVED_BLOCK_SIZE)
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi new_block_size = MAIL_CACHE_MAX_RESERVED_BLOCK_SIZE;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi if ((uint32_t)-1 - hdr->used_file_size >= new_block_size) {
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi block_size = new_block_size;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi ctx->last_grow_size = new_block_size;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi }
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi }
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi if (mail_cache_grow_file(ctx->cache, block_size) < 0)
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi return -1;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi if (ctx->reserved_space_offset + ctx->reserved_space ==
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi hdr->used_file_size) {
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi /* we can simply grow it */
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi /* grow reservation. it's probably the last one in the buffer,
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi but it's not guarateed because we might have used holes
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi as well */
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi reservations = array_get_modifiable(&ctx->reservations, &count);
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi do {
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi i_assert(count > 0);
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi count--;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi } while (reservations[count].offset +
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi reservations[count].size != hdr->used_file_size);
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi reservations[count].size += block_size;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi ctx->reserved_space += block_size;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi } else {
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi mail_cache_transaction_add_reservation(ctx, hdr->used_file_size,
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi block_size);
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi }
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi cache->hdr_modified = TRUE;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi hdr->used_file_size = ctx->reserved_space_offset + ctx->reserved_space;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi return 0;
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi}
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumistatic void
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumimail_cache_free_space(struct mail_cache *cache, uint32_t offset, uint32_t size)
f57517ef962c4b499d229fcf502b43fbd8a54fbeKATOH Yasufumi{
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi struct mail_cache_hole_header hole;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi i_assert(cache->locked);
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (MAIL_CACHE_IS_UNUSABLE(cache))
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi return;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (offset + size == cache->hdr_copy.used_file_size) {
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi /* we can just set used_file_size back */
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi cache->hdr_modified = TRUE;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi cache->hdr_copy.used_file_size = offset;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi } else if (size >= MAIL_CACHE_MIN_HOLE_SIZE) {
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi /* set it up as a hole */
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi hole.next_offset = cache->hdr_copy.hole_offset;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi hole.size = size;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi hole.magic = MAIL_CACHE_HOLE_HEADER_MAGIC;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi if (mail_cache_write(cache, &hole, sizeof(hole), offset) < 0)
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi return;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi cache->hdr_copy.deleted_space += size;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi cache->hdr_copy.hole_offset = offset;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi cache->hdr_modified = TRUE;
28f2ab26ea78e929253e38c6e87bb0e3c05112f2KATOH Yasufumi }
}
static int
mail_cache_transaction_free_space(struct mail_cache_transaction_ctx *ctx)
{
bool locked = ctx->cache->locked;
if (ctx->reserved_space == 0)
return 0;
if (!locked) {
if (mail_cache_transaction_lock(ctx) <= 0)
return 0;
}
/* check again - locking might have reopened the cache file */
if (ctx->reserved_space != 0) {
i_assert(ctx->cache_file_seq == ctx->cache->hdr->file_seq);
mail_cache_free_space(ctx->cache, ctx->reserved_space_offset,
ctx->reserved_space);
ctx->reserved_space_offset = 0;
ctx->reserved_space = 0;
}
if (!locked) {
if (mail_cache_unlock(ctx->cache) < 0)
return -1;
}
return 0;
}
static int
mail_cache_transaction_get_space(struct mail_cache_transaction_ctx *ctx,
size_t min_size, size_t max_size,
uint32_t *offset_r, size_t *available_space_r,
bool commit)
{
bool locked = ctx->cache->locked;
uint32_t cache_file_seq;
size_t size;
int ret;
i_assert((min_size & 3) == 0);
i_assert((max_size & 3) == 0);
if (min_size > ctx->reserved_space) {
/* not enough preallocated space in transaction, get more */
cache_file_seq = ctx->cache_file_seq;
if (!locked) {
if ((ret = mail_cache_transaction_lock(ctx)) <= 0)
return ret;
}
ret = mail_cache_transaction_reserve_more(ctx, max_size,
commit);
if (!locked) {
if (mail_cache_unlock(ctx->cache) < 0)
return -1;
}
if (ret < 0)
return -1;
if (cache_file_seq != ctx->cache_file_seq) {
/* cache file reopened - need to abort */
return 0;
}
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 */
if (mail_cache_transaction_free_space(ctx) < 0)
return -1;
}
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;
bool 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) {
/* error / couldn't lock / 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 (mail_cache_write(cache, rec, max_size, write_offset) < 0)
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 1;
}
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_modifiable_data(ctx->cache_data, &size);
rec = PTR_OFFSET(data, ctx->prev_pos);
rec->size = size - ctx->prev_pos;
i_assert(rec->size > sizeof(*rec));
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,
MAIL_CACHE_WRITE_BUFFER);
i_array_init(&ctx->cache_data_seq, 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_transaction_ctx *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;
/* Here would be a good place to do fdatasync() to make sure
everything is written before offsets are updated to index.
However it slows down I/O unneededly and we're pretty good at
catching and fixing cache corruption, so we no longer do it. */
if (mail_cache_unlock(cache) < 0)
ret = -1;
mail_cache_transaction_free(_ctx);
return ret;
}
void mail_cache_transaction_rollback(struct mail_cache_transaction_ctx **_ctx)
{
struct mail_cache_transaction_ctx *ctx = *_ctx;
struct mail_cache *cache = ctx->cache;
const struct mail_cache_reservation *reservations;
unsigned int count;
if ((ctx->reserved_space > 0 || array_count(&ctx->reservations) > 0) &&
!MAIL_CACHE_IS_UNUSABLE(cache)) {
if (mail_cache_transaction_lock(ctx) > 0) {
reservations = array_get(&ctx->reservations, &count);
/* 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 */
while (count > 0) {
count--;
mail_cache_free_space(ctx->cache,
reservations[count].offset,
reservations[count].size);
}
(void)mail_cache_unlock(cache);
}
}
mail_cache_transaction_free(_ctx);
}
static int
mail_cache_header_fields_write(struct mail_cache_transaction_ctx *ctx,
const buffer_t *buffer)
{
struct mail_cache *cache = ctx->cache;
size_t size = buffer->used;
uint32_t offset, hdr_offset;
if (mail_cache_transaction_get_space(ctx, size, size,
&offset, NULL, TRUE) <= 0)
return -1;
if (mail_cache_write(cache, buffer->data, size, offset) < 0)
return -1;
if (!cache->index->fsync_disable) {
if (fdatasync(cache->fd) < 0) {
mail_cache_set_syscall_error(cache, "fdatasync()");
return -1;
}
}
if (mail_cache_header_fields_get_next_offset(cache, &hdr_offset) < 0)
return -1;
/* if we rollback the transaction, we must not overwrite this
area because it's already committed after updating the
header offset */
mail_cache_transaction_partial_commit(ctx, offset, size);
/* after it's guaranteed to be in disk, update header offset */
offset = mail_index_uint32_to_offset(offset);
if (mail_cache_write(cache, &offset, sizeof(offset), hdr_offset) < 0)
return -1;
if (hdr_offset == offsetof(struct mail_cache_header,
field_header_offset)) {
/* we're adding the first field. hdr_copy needs to be kept
in sync so unlocking won't overwrite it. */
cache->hdr_copy.field_header_offset = hdr_offset;
}
return 0;
}
static int mail_cache_header_add_field(struct mail_cache_transaction_ctx *ctx,
unsigned int field_idx)
{
struct mail_cache *cache = ctx->cache;
buffer_t *buffer;
int ret;
if ((ret = mail_cache_transaction_lock(ctx)) <= 0) {
/* create the cache file if it doesn't exist yet */
if (ctx->tried_compression)
return -1;
ctx->tried_compression = TRUE;
if (mail_cache_compress(cache, ctx->trans) < 0)
return -1;
if ((ret = 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) {
(void)mail_cache_unlock(cache);
return -1;
}
/* update these only after reading */
cache->fields[field_idx].last_used = ioloop_time;
cache->fields[field_idx].used = TRUE;
if (cache->field_file_map[field_idx] != (uint32_t)-1) {
/* it was already added */
if (mail_cache_unlock(cache) < 0)
return -1;
return 0;
}
t_push();
buffer = buffer_create_dynamic(pool_datastack_create(), 256);
mail_cache_header_fields_get(cache, buffer);
ret = mail_cache_header_fields_write(ctx, buffer);
t_pop();
if (ret == 0) {
/* we wrote all the headers, so there are no pending changes */
cache->field_header_write_pending = FALSE;
ret = mail_cache_header_fields_read(cache);
}
if (ret == 0 && cache->field_file_map[field_idx] == (uint32_t)-1) {
mail_index_set_error(cache->index,
"Cache file %s: Newly added field got "
"lost unexpectedly", cache->filepath);
ret = -1;
}
if (mail_cache_unlock(cache) < 0)
ret = -1;
return ret;
}
void mail_cache_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq,
unsigned int field_idx, const void *data, size_t data_size)
{
uint32_t file_field, data_size32;
unsigned int fixed_size;
size_t full_size;
i_assert(field_idx < ctx->cache->fields_count);
i_assert(data_size < (uint32_t)-1);
if (ctx->cache->fields[field_idx].field.decision ==
(MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED))
return;
if (ctx->cache_file_seq == 0) {
if (!ctx->cache->opened)
(void)mail_cache_open_and_verify(ctx->cache);
if (MAIL_CACHE_IS_UNUSABLE(ctx->cache))
return;
ctx->cache_file_seq = ctx->cache->hdr->file_seq;
}
file_field = ctx->cache->field_file_map[field_idx];
if (file_field == (uint32_t)-1) {
/* we'll have to add this field to headers */
if (mail_cache_header_add_field(ctx, field_idx) < 0)
return;
file_field = ctx->cache->field_file_map[field_idx];
i_assert(file_field != (uint32_t)-1);
}
mail_cache_decision_add(ctx->view, seq, field_idx);
fixed_size = ctx->cache->fields[field_idx].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;
}
/* remember that this value exists, in case we try to look it up */
buffer_write(ctx->view->cached_exists_buf, field_idx,
&ctx->view->cached_exists_value, 1);
full_size = (data_size + 3) & ~3;
if (fixed_size == (unsigned int)-1)
full_size += sizeof(data_size32);
if (ctx->cache_data->used + full_size >
buffer_get_size(ctx->cache_data) && ctx->prev_pos > 0) {
/* time to flush our buffer. if flushing fails because the
cache file had been compressed and was reopened, return
without adding the cached data since cache_data buffer
doesn't contain the cache_rec anymore. */
if (mail_cache_transaction_flush(ctx) <= 0) {
/* make sure the transaction is reset, so we don't
constantly try to flush for each call to this
function */
mail_cache_transaction_reset(ctx);
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_zero(ctx->cache_data, 4 - (data_size & 3));
}
bool mail_cache_field_want_add(struct mail_cache_transaction_ctx *ctx,
uint32_t seq, unsigned int field_idx)
{
enum mail_cache_decision_type decision;
if (!ctx->cache->opened)
(void)mail_cache_open_and_verify(ctx->cache);
decision = mail_cache_field_get_decision(ctx->view->cache, field_idx);
if ((decision & ~MAIL_CACHE_DECISION_FORCED) == MAIL_CACHE_DECISION_NO)
return FALSE;
return mail_cache_field_exists(ctx->view, seq, field_idx) == 0;
}
bool mail_cache_field_can_add(struct mail_cache_transaction_ctx *ctx,
uint32_t seq, unsigned int field_idx)
{
enum mail_cache_decision_type decision;
if (!ctx->cache->opened)
(void)mail_cache_open_and_verify(ctx->cache);
decision = mail_cache_field_get_decision(ctx->view->cache, field_idx);
if (decision == (MAIL_CACHE_DECISION_FORCED | MAIL_CACHE_DECISION_NO))
return FALSE;
return mail_cache_field_exists(ctx->view, seq, field_idx) == 0;
}
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);
return mail_cache_write(cache, &old_offset, sizeof(old_offset),
new_offset);
}
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 *rec;
ARRAY_TYPE(uint32_t) looping_offsets;
int ret = -1;
i_assert(cache->locked);
/* 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. */
t_push();
t_array_init(&looping_offsets, 8);
array_append(&looping_offsets, &offset, 1);
while (mail_cache_get_record(cache, offset, &rec) == 0) {
cache->hdr_copy.deleted_space += rec->size;
offset = rec->prev_offset;
if (offset == 0) {
/* successfully got to the end of the list */
ret = 0;
break;
}
if (mail_cache_track_loops(&looping_offsets, offset)) {
mail_cache_set_corrupted(cache,
"record list is circular");
break;
}
}
t_pop();
cache->hdr_modified = TRUE;
return ret;
}