mail-cache-transaction.c revision d24daa83c25063ef12b524c9ffcc9ecca34dadb9
76b43e4417bab52e913da39b5f5bc2a130d3f149Timo Sirainen/* Copyright (c) 2003-2007 Dovecot authors, see the included COPYING file */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ARRAY_DEFINE(reservations, struct mail_cache_reservation);
1285518f4f8905f22f5812d022a9f75b51752ed4Timo Sirainen uint32_t reserved_space_offset, reserved_space;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainenstatic int mail_cache_link_unlocked(struct mail_cache *cache,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_cache_get_transaction(struct mail_cache_view *view,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx = i_new(struct mail_cache_transaction_ctx, 1);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen view->trans_view = mail_index_transaction_open_updated_view(t);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic void mail_cache_transaction_reset(struct mail_cache_transaction_ctx *ctx)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen ctx->cache_file_seq = MAIL_CACHE_IS_UNUSABLE(ctx->cache) ? 0 :
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen mail_index_ext_set_reset_id(ctx->trans, ctx->cache->ext_id,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_cache_transaction_free(struct mail_cache_transaction_ctx **_ctx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mail_cache_transaction_ctx *ctx = *_ctx;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx->view->trans_seq1 = ctx->view->trans_seq2 = 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_index_view_close(&ctx->view->trans_view);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_cache_transaction_compress(struct mail_cache_transaction_ctx *ctx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen MAIL_CACHE_IS_UNUSABLE(cache) ? 0 : cache->hdr->file_seq;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ret = mail_index_transaction_commit(&trans, &log_file_seq,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_cache_transaction_open_if_needed(struct mail_cache_transaction_ctx *ctx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* see if we should try to reopen the cache file */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for (i = 0;; i++) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (!mail_index_map_get_ext_idx(cache->index->map,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ext = array_idx(&cache->index->map->extensions, idx);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (ext->reset_id == cache->hdr->file_seq || i == 2)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* index offsets don't match the cache file */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* the cache file appears to be too old.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen reopening should help. */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* cache file sequence might be broken. it's also possible
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen that it was just compressed and we just haven't yet seen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen the changes in index. try if refreshing index helps.
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if not, compress the cache file. */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (i == 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* get the latest reset ID */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (mail_index_refresh(ctx->cache->index) < 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic int mail_cache_transaction_lock(struct mail_cache_transaction_ctx *ctx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if ((ret = mail_cache_lock(cache, FALSE)) <= 0) {
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen if (!ctx->tried_compression && MAIL_CACHE_IS_UNUSABLE(cache)) {
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen if (!MAIL_CACHE_IS_UNUSABLE(cache) && ctx->cache_file_seq == 0) {
3e28b527dd6048a40684afd29cff0ee008fc0014Timo Sirainen } else if (ctx->cache_file_seq != cache->hdr->file_seq) {
ccffb125d94adff0ad776de5a96e22f864d6fb0aTimo Sirainenstatic int mail_cache_grow_file(struct mail_cache *cache, size_t size)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* grow the file */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen new_fsize = cache->hdr_copy.used_file_size + size;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen grow_size = new_fsize / 100 * MAIL_CACHE_GROW_PERCENTAGE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_cache_set_syscall_error(cache, "fstat()");
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (file_set_size(cache->fd, new_fsize) < 0) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen mail_cache_set_syscall_error(cache, "file_set_size()");
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic bool mail_cache_unlink_hole(struct mail_cache *cache, size_t size,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mail_cache_header *hdr = &cache->hdr_copy;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen while (offset != 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (pread_full(cache->fd, &hole, sizeof(hole), offset) <= 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_cache_set_syscall_error(cache, "pread_full()");
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen if (hole.magic != MAIL_CACHE_HOLE_HEADER_MAGIC) {
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen "Invalid magic in hole header");
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen if (mail_cache_write(cache, &hole.next_offset,
b221779c191d1fb5fa7eb03907e62d39d1edeb08Timo Sirainenmail_cache_transaction_add_reservation(struct mail_cache_transaction_ctx *ctx,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_cache_transaction_partial_commit(struct mail_cache_transaction_ctx *ctx,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int i, count;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (offset + size == ctx->cache->hdr_copy.used_file_size &&
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen offset + size == ctx->reserved_space_offset) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen res = array_get_modifiable(&ctx->reservations, &count);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for (i = 0; i < count; i++) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_cache_transaction_reserve_more(struct mail_cache_transaction_ctx *ctx,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mail_cache_header *hdr = &cache->hdr_copy;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int count;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (mail_cache_unlink_hole(cache, block_size, &hole)) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* found a large enough hole. */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_cache_transaction_add_reservation(ctx, hole.next_offset,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* mail_cache_unlink_hole() could have noticed corruption */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if ((uint32_t)-1 - hdr->used_file_size < block_size) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_index_set_error(cache->index, "Cache file too large: %s",
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (!commit && block_size < MAIL_CACHE_MAX_RESERVED_BLOCK_SIZE) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* allocate some more space than we need */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen size_t new_block_size = (block_size + ctx->last_grow_size) * 2;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (new_block_size > MAIL_CACHE_MAX_RESERVED_BLOCK_SIZE)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen new_block_size = MAIL_CACHE_MAX_RESERVED_BLOCK_SIZE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if ((uint32_t)-1 - hdr->used_file_size >= new_block_size) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (mail_cache_grow_file(ctx->cache, block_size) < 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (ctx->reserved_space_offset + ctx->reserved_space ==
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* we can simply grow it */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* grow reservation. it's probably the last one in the buffer,
dd171dccbe98fc63ca737e6e4e8edbeb601e5cbdTimo Sirainen but it's not guarateed because we might have used holes
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen reservations = array_get_modifiable(&ctx->reservations, &count);
dd171dccbe98fc63ca737e6e4e8edbeb601e5cbdTimo Sirainen reservations[count].size != hdr->used_file_size);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_cache_transaction_add_reservation(ctx, hdr->used_file_size,
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen hdr->used_file_size = ctx->reserved_space_offset + ctx->reserved_space;
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainenmail_cache_free_space(struct mail_cache *cache, uint32_t offset, uint32_t size)
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen if (offset + size == cache->hdr_copy.used_file_size) {
1412a091183dc0e5d6ea4f403a5cd4f4cd5c7301Timo Sirainen /* we can just set used_file_size back */
61dca057fe86fd5ae57f5106f8f049b7287d78cdTimo Sirainen } else if (size >= MAIL_CACHE_MIN_HOLE_SIZE) {
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen /* set it up as a hole */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen hole.next_offset = cache->hdr_copy.hole_offset;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (mail_cache_write(cache, &hole, sizeof(hole), offset) < 0)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenmail_cache_transaction_free_space(struct mail_cache_transaction_ctx *ctx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* check again - locking might have reopened the cache file */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_assert(ctx->cache_file_seq == ctx->cache->hdr->file_seq);
dd171dccbe98fc63ca737e6e4e8edbeb601e5cbdTimo Sirainen mail_cache_free_space(ctx->cache, ctx->reserved_space_offset,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_cache_transaction_get_space(struct mail_cache_transaction_ctx *ctx,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen uint32_t *offset_r, size_t *available_space_r,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* not enough preallocated space in transaction, get more */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if ((ret = mail_cache_transaction_lock(ctx)) <= 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ret = mail_cache_transaction_reserve_more(ctx, max_size,
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen /* cache file reopened - need to abort */
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen /* final commit - see if we can free the rest of the
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen reserved space */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (mail_cache_transaction_free_space(ctx) < 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_cache_transaction_update_index(struct mail_cache_transaction_ctx *ctx,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* write the cache_offsets to index file. records' prev_offset
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen is updated to point to old cache record when index is being
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_index_update_ext(ctx->trans, seq[i], cache->ext_id,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* we added records for this message multiple
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen times in this same uncommitted transaction.
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen only the new one will be written to
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen transaction log, we need to do the linking
1285518f4f8905f22f5812d022a9f75b51752ed4Timo Sirainen ourself here. */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (mail_cache_link_unlocked(cache, old_offset,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* if we're combining multiple transactions,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen make sure the one with the smallest offset
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen is written into index. this is required for
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen non-file-mmaped cache to work properly. */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenmail_cache_transaction_flush(struct mail_cache_transaction_ctx *ctx)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen const struct mail_cache_record *rec, *tmp_rec;
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen uint32_t write_offset, write_size, rec_pos, seq_idx, seq_limit;
5fe06fea9fee0f5e4e9cb49f6866877223f78b85Timo Sirainen /* committing, remove the last dummy record */
5fe06fea9fee0f5e4e9cb49f6866877223f78b85Timo Sirainen buffer_set_used_size(ctx->cache_data, ctx->prev_pos);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen if (ctx->cache_file_seq != ctx->cache->hdr->file_seq) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* cache file reopened - need to abort */
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen rec = buffer_get_data(ctx->cache_data, &size);
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen seq = array_get(&ctx->cache_data_seq, &seq_count);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen for (seq_idx = 0, rec_pos = 0; rec_pos < ctx->prev_pos;) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ret = mail_cache_transaction_get_space(ctx, rec->size,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* error / couldn't lock / cache file reopened */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* see how much we can really write there */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for (size = 0; size + tmp_rec->size <= max_size; ) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* write it to file */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_assert(ctx->cache_file_seq == cache->hdr->file_seq);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (mail_cache_write(cache, rec, max_size, write_offset) < 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (mail_cache_transaction_update_index(ctx, rec, seq,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* drop the written data from buffer */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_cache_transaction_switch_seq(struct mail_cache_transaction_ctx *ctx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* fix record size */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen data = buffer_get_modifiable_data(ctx->cache_data, &size);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen array_append(&ctx->cache_data_seq, &ctx->prev_seq, 1);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen buffer_append(ctx->cache_data, &new_rec, sizeof(new_rec));
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenint mail_cache_transaction_commit(struct mail_cache_transaction_ctx **_ctx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mail_cache_transaction_ctx *ctx = *_ctx;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (!ctx->changes || MAIL_CACHE_IS_UNUSABLE(cache)) {
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen /* Here would be a good place to do fdatasync() to make sure
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen everything is written before offsets are updated to index.
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen However it slows down I/O unneededly and we're pretty good at
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen catching and fixing cache corruption, so we no longer do it. */
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainenvoid mail_cache_transaction_rollback(struct mail_cache_transaction_ctx **_ctx)
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen struct mail_cache_transaction_ctx *ctx = *_ctx;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen const struct mail_cache_reservation *reservations;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int count;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if ((ctx->reserved_space > 0 || array_count(&ctx->reservations) > 0) &&
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen reservations = array_get(&ctx->reservations, &count);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* free flushed data as well. do it from end to
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen beginning so we have a better chance of
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen updating used_file_size instead of adding
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen while (count > 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_cache_header_fields_write(struct mail_cache_transaction_ctx *ctx,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (mail_cache_transaction_get_space(ctx, size, size,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (mail_cache_write(cache, buffer->data, size, offset) < 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_cache_set_syscall_error(cache, "fdatasync()");
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (mail_cache_header_fields_get_next_offset(cache, &hdr_offset) < 0)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* if we rollback the transaction, we must not overwrite this
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen area because it's already committed after updating the
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen header offset */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen mail_cache_transaction_partial_commit(ctx, offset, size);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* after it's guaranteed to be in disk, update header offset */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (mail_cache_write(cache, &offset, sizeof(offset), hdr_offset) < 0)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (hdr_offset == offsetof(struct mail_cache_header,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* we're adding the first field. hdr_copy needs to be kept
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen in sync so unlocking won't overwrite it. */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen cache->hdr_copy.field_header_offset = hdr_offset;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen cache->hdr_ro_copy.field_header_offset = hdr_offset;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic int mail_cache_header_add_field(struct mail_cache_transaction_ctx *ctx,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen unsigned int i;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* we want to avoid adding all the fields one by one to the cache file,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen so just add all of them at once in here. the unused ones get dropped
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen later when compressing. */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if ((ret = mail_cache_transaction_lock(ctx)) <= 0) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* if we compressed the cache, the field should be there now.
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen it's however possible that someone else just compressed it
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen and we only reopened the cache file. */
28cddf411c475eb8bb84b4023398bb12346ce5adTimo Sirainen if (cache->field_file_map[field_idx] != (uint32_t)-1)
28cddf411c475eb8bb84b4023398bb12346ce5adTimo Sirainen /* need to add it */
28cddf411c475eb8bb84b4023398bb12346ce5adTimo Sirainen if ((ret = mail_cache_transaction_lock(ctx)) <= 0)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* re-read header to make sure we don't lose any fields. */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (mail_cache_header_fields_read(cache) < 0) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (cache->field_file_map[field_idx] != (uint32_t)-1) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* it was already added */
28cddf411c475eb8bb84b4023398bb12346ce5adTimo Sirainen buffer = buffer_create_dynamic(pool_datastack_create(), 256);
28cddf411c475eb8bb84b4023398bb12346ce5adTimo Sirainen ret = mail_cache_header_fields_write(ctx, buffer);
a1aaf11831cab8346d6d0dc702e37b3f1d95eb43Timo Sirainen /* we wrote all the headers, so there are no pending changes */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (ret == 0 && cache->field_file_map[field_idx] == (uint32_t)-1) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen "Cache file %s: Newly added field got "
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenvoid mail_cache_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen unsigned int field_idx, const void *data, size_t data_size)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen i_assert(field_idx < ctx->cache->fields_count);
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen if (ctx->cache->fields[field_idx].field.decision ==
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED))
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen ctx->cache_file_seq = ctx->cache->hdr->file_seq;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen } else if (!MAIL_CACHE_IS_UNUSABLE(ctx->cache) &&
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen ctx->cache_file_seq != ctx->cache->hdr->file_seq) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* cache was compressed within this transaction */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen file_field = ctx->cache->field_file_map[field_idx];
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (MAIL_CACHE_IS_UNUSABLE(ctx->cache) || file_field == (uint32_t)-1) {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* we'll have to add this field to headers */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (mail_cache_header_add_field(ctx, field_idx) < 0)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen ctx->cache_file_seq = ctx->cache->hdr->file_seq;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen file_field = ctx->cache->field_file_map[field_idx];
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen mail_cache_decision_add(ctx->view, seq, field_idx);
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen fixed_size = ctx->cache->fields[field_idx].field.field_size;
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen i_assert(fixed_size == (unsigned int)-1 || fixed_size == data_size);
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen /* remember roughly what we have modified, so cache lookups can
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen look into transactions to see changes. */
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen if (seq < ctx->view->trans_seq1 || ctx->view->trans_seq1 == 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* remember that this value exists, in case we try to look it up */
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen buffer_write(ctx->view->cached_exists_buf, field_idx,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen buffer_get_size(ctx->cache_data) && ctx->prev_pos > 0) {
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen /* time to flush our buffer. if flushing fails because the
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen cache file had been compressed and was reopened, return
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen without adding the cached data since cache_data buffer
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen doesn't contain the cache_rec anymore. */
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen /* make sure the transaction is reset, so we don't
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen constantly try to flush for each call to this
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen buffer_append(ctx->cache_data, &file_field, sizeof(file_field));
return FALSE;
return FALSE;
t_push();
if (offset == 0) {
ret = 0;
t_pop();
return ret;