bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainen MODULE_CONTEXT(obj, cache_mail_index_transaction_module)
c04adbeb0f214dc323ced079c7380fe9a226cdc5Aki Tuomi MODULE_CONTEXT_REQUIRE(obj, cache_mail_index_transaction_module)
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainen union mail_index_transaction_module_context module_ctx;
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen ARRAY(struct mail_cache_transaction_rec) cache_data_seq;
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainenstatic MODULE_CONTEXT_DEFINE_INIT(cache_mail_index_transaction_module,
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainenstatic int mail_cache_transaction_lock(struct mail_cache_transaction_ctx *ctx);
127f99adeb10d9f9e06896f97dc187eac4ca8852Timo Sirainenstatic size_t mail_cache_transaction_update_last_rec_size(struct mail_cache_transaction_ctx *ctx);
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainenstatic void mail_index_transaction_cache_reset(struct mail_index_transaction *t)
c04adbeb0f214dc323ced079c7380fe9a226cdc5Aki Tuomi struct mail_cache_transaction_ctx *ctx = CACHE_TRANS_CONTEXT_REQUIRE(t);
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainen struct mail_index_transaction_vfuncs super = ctx->super;
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainenmail_index_transaction_cache_commit(struct mail_index_transaction *t,
9f19a50d5966643c4d1c5ca06868ac2ad31bc4d5Timo Sirainen struct mail_index_transaction_commit_result *result_r)
c04adbeb0f214dc323ced079c7380fe9a226cdc5Aki Tuomi struct mail_cache_transaction_ctx *ctx = CACHE_TRANS_CONTEXT_REQUIRE(t);
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainen struct mail_index_transaction_vfuncs super = ctx->super;
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen /* a failed cache commit isn't important enough to fail the entire
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen index transaction, so we'll just ignore it */
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainenmail_index_transaction_cache_rollback(struct mail_index_transaction *t)
c04adbeb0f214dc323ced079c7380fe9a226cdc5Aki Tuomi struct mail_cache_transaction_ctx *ctx = CACHE_TRANS_CONTEXT_REQUIRE(t);
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainen struct mail_index_transaction_vfuncs super = ctx->super;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenmail_cache_get_transaction(struct mail_cache_view *view,
61b6d1256936065321153bcd9228b9e45d95c9abTimo Sirainen ctx = !cache_mail_index_transaction_module.id.module_id_set ? NULL :
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ctx = i_new(struct mail_cache_transaction_ctx, 1);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen view->trans_view = mail_index_transaction_open_updated_view(t);
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainen t->v.reset = mail_index_transaction_cache_reset;
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainen t->v.commit = mail_index_transaction_cache_commit;
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainen t->v.rollback = mail_index_transaction_cache_rollback;
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainen MODULE_CONTEXT_SET(t, cache_mail_index_transaction_module, ctx);
61b6d1256936065321153bcd9228b9e45d95c9abTimo Sirainenvoid mail_cache_transaction_reset(struct mail_cache_transaction_ctx *ctx)
1a5fcc972dbadfe7959011b8ad422707e2dfc19fTimo Sirainen ctx->cache_file_seq = MAIL_CACHE_IS_UNUSABLE(ctx->cache) ? 0 :
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainen mail_index_ext_set_reset_id(ctx->trans, ctx->cache->ext_id,
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainenvoid mail_cache_transaction_rollback(struct mail_cache_transaction_ctx **_ctx)
6f73af3a3a6ee900c7e736874587968d76a20bc0Timo Sirainen struct mail_cache_transaction_ctx *ctx = *_ctx;
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen /* we already wrote to the cache file. we can't (or don't want
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen to) delete that data, so just mark it as deleted space */
5e40ed3f0a2c2acddc9b8eab59670c7a850114c5Timo Sirainen MODULE_CONTEXT_UNSET(ctx->trans, cache_mail_index_transaction_module);
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen ctx->view->trans_seq1 = ctx->view->trans_seq2 = 0;
6f73af3a3a6ee900c7e736874587968d76a20bc0Timo Sirainen mail_index_view_close(&ctx->view->trans_view);
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen if (array_is_created(&ctx->cache_data_wanted_seqs))
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainenmail_cache_transaction_compress(struct mail_cache_transaction_ctx *ctx)
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen MAIL_CACHE_IS_UNUSABLE(cache) ? 0 : cache->hdr->file_seq;
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen if (mail_cache_compress(cache, trans, &lock) < 0) {
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainenmail_cache_transaction_open_if_needed(struct mail_cache_transaction_ctx *ctx)
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainen /* see if we should try to reopen the cache file */
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen for (i = 0;; i++) {
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen if (!mail_index_map_get_ext_idx(cache->index->map,
8fcaba5977f7d596632df1ed2af8541e5f154258Timo Sirainen /* index doesn't have a cache extension, but the cache
8fcaba5977f7d596632df1ed2af8541e5f154258Timo Sirainen file exists (corrupted indexes fixed?). fix it. */
8fcaba5977f7d596632df1ed2af8541e5f154258Timo Sirainen ext = array_idx(&cache->index->map->extensions, idx);
8fcaba5977f7d596632df1ed2af8541e5f154258Timo Sirainen if (ext->reset_id == cache->hdr->file_seq || i == 2)
8fcaba5977f7d596632df1ed2af8541e5f154258Timo Sirainen /* index offsets don't match the cache file */
8fcaba5977f7d596632df1ed2af8541e5f154258Timo Sirainen /* the cache file appears to be too old.
8fcaba5977f7d596632df1ed2af8541e5f154258Timo Sirainen reopening should help. */
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen /* cache file sequence might be broken. it's also possible
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen that it was just compressed and we just haven't yet seen
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen the changes in index. try if refreshing index helps.
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen if not, compress the cache file. */
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen if (i == 0) {
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen /* get the latest reset ID */
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen if (mail_index_refresh(ctx->cache->index) < 0)
a2ce2eb4c266e2854fd34416ea5cfbe05dfd3971Timo Sirainenstatic int mail_cache_transaction_lock(struct mail_cache_transaction_ctx *ctx)
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainen if (!ctx->tried_compression && MAIL_CACHE_IS_UNUSABLE(cache)) {
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainen } else if (ctx->cache_file_seq != cache->hdr->file_seq) {
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainenmail_cache_transaction_lookup_rec(struct mail_cache_transaction_ctx *ctx,
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen unsigned int seq,
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen const struct mail_cache_transaction_rec *recs;
3da9825732c865b8ea11ea070e5b6f881ffae424Timo Sirainen if (!MAIL_INDEX_IS_IN_MEMORY(ctx->cache->index) &&
3da9825732c865b8ea11ea070e5b6f881ffae424Timo Sirainen ctx->cache_file_seq != ctx->cache->hdr->file_seq)) {
1c783b6d9cbdf0cdb84507472f0aa6a50a1861cdTimo Sirainen /* Cache was compressed during this transaction. We can't
1c783b6d9cbdf0cdb84507472f0aa6a50a1861cdTimo Sirainen safely use the data anymore, since its fields won't match
1c783b6d9cbdf0cdb84507472f0aa6a50a1861cdTimo Sirainen cache->file_fields_map. */
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen recs = array_get(&ctx->cache_data_seq, &count);
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen return CONST_PTR_OFFSET(ctx->cache_data->data,
127f99adeb10d9f9e06896f97dc187eac4ca8852Timo Sirainen /* update the unfinished record's (temporary) size and
127f99adeb10d9f9e06896f97dc187eac4ca8852Timo Sirainen mail_cache_transaction_update_last_rec_size(ctx);
127f99adeb10d9f9e06896f97dc187eac4ca8852Timo Sirainen return CONST_PTR_OFFSET(ctx->cache_data->data,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenmail_cache_transaction_update_index(struct mail_cache_transaction_ctx *ctx,
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen const struct mail_cache_record *rec = ctx->cache_data->data;
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen const struct mail_cache_transaction_rec *recs;
16c28dd75976f94acc4940d6ba68b6cd4853aac7Timo Sirainen mail_index_ext_using_reset_id(ctx->trans, ctx->cache->ext_id,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* write the cache_offsets to index file. records' prev_offset
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen is updated to point to old cache record when index is being
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen recs = array_get(&ctx->cache_data_seq, &seq_count);
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen for (i = 0; i < seq_count; i++) {
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen mail_index_update_ext(ctx->trans, recs[i].seq, cache->ext_id,
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainenmail_cache_link_records(struct mail_cache_transaction_ctx *ctx,
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen const struct mail_cache_transaction_rec *recs;
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen uint32_t i, seq_count, reset_id, prev_offset, *offsetp;
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen recs = array_get(&ctx->cache_data_seq, &seq_count);
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen rec = buffer_get_modifiable_data(ctx->cache_data, NULL);
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen for (i = 0; i < seq_count; i++) {
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen mail_index_lookup_ext_full(ctx->view->trans_view, recs[i].seq,
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen if (prev_offsetp == NULL || *prev_offsetp == 0)
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen else if (mail_index_ext_get_reset_id(ctx->view->trans_view, map,
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen "Cache record offset points outside existing file");
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen /* link this record to previous one */
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen ctx->cache->hdr_copy.continued_record_count++;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenmail_cache_transaction_flush(struct mail_cache_transaction_ctx *ctx)
330d54e9e3bf076efe91f101c562d1268160b128Timo Sirainen /* we had done some changes, but they were aborted. */
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen i_assert(ctx->last_rec_pos <= ctx->cache_data->used);
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen /* we need to get the final write offset for linking records */
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen mail_cache_set_syscall_error(ctx->cache, "fstat()");
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen } else if ((uint32_t)-1 < st.st_size + ctx->last_rec_pos) {
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen mail_cache_set_corrupted(ctx->cache, "Cache file too large");
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen if (mail_cache_link_records(ctx, write_offset) < 0)
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen /* write to cache file */
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen mail_cache_append(ctx->cache, ctx->cache_data->data,
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen /* update records' cache offsets to index */
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen ret = mail_cache_transaction_update_index(ctx, write_offset);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* drop the written data from buffer */
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen ctx->cache_data, ctx->last_rec_pos, (size_t)-1);
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainenmail_cache_transaction_drop_unwanted(struct mail_cache_transaction_ctx *ctx,
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen recs = array_get_modifiable(&ctx->cache_data_seq, &count);
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen /* find out how many records to delete. delete all unwanted sequences,
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen and if that's not enough delete some more. */
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen for (i = 0; i < count; i++) {
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen if (seq_range_exists(&ctx->cache_data_wanted_seqs, recs[i].seq)) {
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen /* we're going to forcibly delete it - remove it also
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen from the array since it's no longer useful there */
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen seq_range_array_remove(&ctx->cache_data_wanted_seqs,
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen for (; i < count; i++)
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen array_delete(&ctx->cache_data_seq, 0, deleted_count);
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen buffer_delete(ctx->cache_data, 0, deleted_space);
127f99adeb10d9f9e06896f97dc187eac4ca8852Timo Sirainenmail_cache_transaction_update_last_rec_size(struct mail_cache_transaction_ctx *ctx)
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen data = buffer_get_modifiable_data(ctx->cache_data, &size);
127f99adeb10d9f9e06896f97dc187eac4ca8852Timo Sirainenmail_cache_transaction_update_last_rec(struct mail_cache_transaction_ctx *ctx)
127f99adeb10d9f9e06896f97dc187eac4ca8852Timo Sirainen size = mail_cache_transaction_update_last_rec_size(ctx);
67bbcd664bebce9a507a49c67273be4814d07c97Timo Sirainen if (size > ctx->cache->index->optimization_set.cache.record_max_size) {
330d54e9e3bf076efe91f101c562d1268160b128Timo Sirainen buffer_set_used_size(ctx->cache_data, ctx->last_rec_pos);
131b073bdc3650083b00616dc778dd3017c2bbb5Timo Sirainen if (ctx->min_seq > ctx->prev_seq || ctx->min_seq == 0)
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen trans_rec = array_append_space(&ctx->cache_data_seq);
d33fc6c584718efd46159e1d8f46488b9dfc66f5Timo Sirainen trans_rec->cache_data_pos = ctx->last_rec_pos;
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainenmail_cache_transaction_switch_seq(struct mail_cache_transaction_ctx *ctx)
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen /* update previously added cache record's size */
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen i_array_init(&ctx->cache_data_wanted_seqs, 32);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen buffer_append(ctx->cache_data, &new_rec, sizeof(new_rec));
6f73af3a3a6ee900c7e736874587968d76a20bc0Timo Sirainenint mail_cache_transaction_commit(struct mail_cache_transaction_ctx **_ctx)
6f73af3a3a6ee900c7e736874587968d76a20bc0Timo Sirainen struct mail_cache_transaction_ctx *ctx = *_ctx;
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen /* successfully wrote everything */
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen /* Here would be a good place to do fdatasync() to make sure
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen everything is written before offsets are updated to index.
19557f192d37cd54a1a090a8a26d9d47265e4413Aki Tuomi However it slows down I/O needlessly and we're pretty good
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen at catching and fixing cache corruption, so we no longer do
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainenmail_cache_header_fields_write(struct mail_cache_transaction_ctx *ctx,
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen if (mail_cache_append(cache, buffer->data, buffer->used, &offset) < 0)
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen if (cache->index->fsync_mode == FSYNC_MODE_ALWAYS) {
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen mail_cache_set_syscall_error(cache, "fdatasync()");
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen /* find offset to the previous header's "next_offset" field */
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen if (mail_cache_header_fields_get_next_offset(cache, &hdr_offset) < 0)
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen /* update the next_offset offset, so our new header will be found */
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen if (mail_cache_write(cache, &offset, sizeof(offset), hdr_offset) < 0)
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen if (hdr_offset == offsetof(struct mail_cache_header,
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen /* we're adding the first field. hdr_copy needs to be kept
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen in sync so unlocking won't overwrite it. */
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen cache->hdr_copy.field_header_offset = hdr_offset;
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen cache->hdr_ro_copy.field_header_offset = hdr_offset;
ddbad7a661c0663fafd2b79393efa85f840d6af6Timo Sirainenstatic void mail_cache_mark_adding(struct mail_cache *cache, bool set)
fe6cf42464c36ab281c0e0740f0255be77453670Timo Sirainen unsigned int i;
fe6cf42464c36ab281c0e0740f0255be77453670Timo Sirainen /* we want to avoid adding all the fields one by one to the cache file,
fe6cf42464c36ab281c0e0740f0255be77453670Timo Sirainen so just add all of them at once in here. the unused ones get dropped
fe6cf42464c36ab281c0e0740f0255be77453670Timo Sirainen later when compressing. */
ddbad7a661c0663fafd2b79393efa85f840d6af6Timo Sirainenstatic int mail_cache_header_add_field(struct mail_cache_transaction_ctx *ctx,
e6d4f540bf5c3b7ef5d6e154b217a2422210048cTimo Sirainen unsigned int,
ac90bc1130bc014e41c47a1eb81dc1a3d1108115Timo Sirainen /* if we compressed the cache, the field should be there now.
ac90bc1130bc014e41c47a1eb81dc1a3d1108115Timo Sirainen it's however possible that someone else just compressed it
ac90bc1130bc014e41c47a1eb81dc1a3d1108115Timo Sirainen and we only reopened the cache file. */
ac90bc1130bc014e41c47a1eb81dc1a3d1108115Timo Sirainen if (cache->field_file_map[field_idx] != (uint32_t)-1)
ac90bc1130bc014e41c47a1eb81dc1a3d1108115Timo Sirainen /* need to add it */
5ebddd2d812296900bc255b24bcd508878784c37Timo Sirainen /* re-read header to make sure we don't lose any fields. */
5ebddd2d812296900bc255b24bcd508878784c37Timo Sirainen if (mail_cache_header_fields_read(cache) < 0) {
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen if (cache->field_file_map[field_idx] != (uint32_t)-1) {
5ebddd2d812296900bc255b24bcd508878784c37Timo Sirainen /* it was already added */
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen ret = mail_cache_header_fields_write(ctx, buffer);
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen /* we wrote all the headers, so there are no pending changes */
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen if (ret == 0 && cache->field_file_map[field_idx] == (uint32_t)-1) {
8c02331f9f569d8b30e74b6bc8550734d65f9daeTimo Sirainen "Cache file %s: Newly added field got "
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainenvoid mail_cache_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq,
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen unsigned int field_idx, const void *data, size_t data_size)
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen i_assert(field_idx < ctx->cache->fields_count);
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen if (ctx->cache->fields[field_idx].field.decision ==
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED))
b387ee112301fef59d16ab3b120e3821cd0c70dfTimo Sirainen ctx->cache_file_seq = ctx->cache->hdr->file_seq;
da5dd6f1141e8b134199e1a2a23af4e05173464aTimo Sirainen } else if (!MAIL_CACHE_IS_UNUSABLE(ctx->cache) &&
da5dd6f1141e8b134199e1a2a23af4e05173464aTimo Sirainen ctx->cache_file_seq != ctx->cache->hdr->file_seq) {
b159b7fb9740b6e37238016d8395a351de498d50Timo Sirainen /* cache was compressed within this transaction */
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen file_field = ctx->cache->field_file_map[field_idx];
17118d434fcd48b0f0211bdac2747276f0b05223Timo Sirainen if (MAIL_CACHE_IS_UNUSABLE(ctx->cache) || file_field == (uint32_t)-1) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* we'll have to add this field to headers */
ddbad7a661c0663fafd2b79393efa85f840d6af6Timo Sirainen ret = mail_cache_header_add_field(ctx, field_idx);
91496fd60a7980f4ebdf93cbb099b9db198a0e74Timo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(ctx->cache->index))
91496fd60a7980f4ebdf93cbb099b9db198a0e74Timo Sirainen ctx->cache_file_seq = ctx->cache->hdr->file_seq;
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen file_field = ctx->cache->field_file_map[field_idx];
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen mail_cache_decision_add(ctx->view, seq, field_idx);
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen fixed_size = ctx->cache->fields[field_idx].field.field_size;
8ae72ad7d0c69e972cfa65d1e2ce4e3e9a8b765cTimo Sirainen i_assert(fixed_size == UINT_MAX || fixed_size == data_size);
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen seq_range_array_add(&ctx->cache_data_wanted_seqs, seq);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* remember roughly what we have modified, so cache lookups can
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen look into transactions to see changes. */
ca316aeb7648d3f1bcf45231f73ddeb1b67a6961Timo Sirainen if (seq < ctx->view->trans_seq1 || ctx->view->trans_seq1 == 0)
988f9ad3b5e973c12453a780effc477031107648Timo Sirainen /* remember that this value exists, in case we try to look it up */
988f9ad3b5e973c12453a780effc477031107648Timo Sirainen buffer_write(ctx->view->cached_exists_buf, field_idx,
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen if (ctx->cache_data->used + full_size > MAIL_CACHE_MAX_WRITE_BUFFER &&
dd0ba1bab2c1b89c7e063fa45d156fa72b8260d5Timo Sirainen /* time to flush our buffer. if flushing fails because the
dd0ba1bab2c1b89c7e063fa45d156fa72b8260d5Timo Sirainen cache file had been compressed and was reopened, return
dd0ba1bab2c1b89c7e063fa45d156fa72b8260d5Timo Sirainen without adding the cached data since cache_data buffer
dd0ba1bab2c1b89c7e063fa45d156fa72b8260d5Timo Sirainen doesn't contain the cache_rec anymore. */
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(ctx->cache->index)) {
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen /* just drop the old data to free up memory */
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen mail_cache_transaction_drop_unwanted(ctx, space_needed);
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen } else if (mail_cache_transaction_flush(ctx) < 0) {
6a669919418a1c7469f9faf55d11b3c723f72cffTimo Sirainen /* make sure the transaction is reset, so we don't
6a669919418a1c7469f9faf55d11b3c723f72cffTimo Sirainen constantly try to flush for each call to this
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen buffer_append(ctx->cache_data, &file_field, sizeof(file_field));
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen buffer_append(ctx->cache_data, data, data_size);
dd0ba1bab2c1b89c7e063fa45d156fa72b8260d5Timo Sirainen buffer_append_zero(ctx->cache_data, 4 - (data_size & 3));
e0127c3244319fffb07bf65a3ed9e4a5d6e555b7Timo Sirainenbool mail_cache_field_want_add(struct mail_cache_transaction_ctx *ctx,
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen decision = mail_cache_field_get_decision(ctx->view->cache, field_idx);
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen /* add it only if it's newer than what we would drop when
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen compressing */
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen mail_cache_get_first_new_seq(ctx->view->view);
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen return mail_cache_field_exists(ctx->view, seq, field_idx) == 0;
e0127c3244319fffb07bf65a3ed9e4a5d6e555b7Timo Sirainenbool mail_cache_field_can_add(struct mail_cache_transaction_ctx *ctx,
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen decision = mail_cache_field_get_decision(ctx->view->cache, field_idx);
e0127c3244319fffb07bf65a3ed9e4a5d6e555b7Timo Sirainen if (decision == (MAIL_CACHE_DECISION_FORCED | MAIL_CACHE_DECISION_NO))
710deabc6b3c305c3a842e7a2e0d173c526d13a7Timo Sirainen return mail_cache_field_exists(ctx->view, seq, field_idx) == 0;
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainenvoid mail_cache_close_mail(struct mail_cache_transaction_ctx *ctx,
93cb78fb1947d34e98efffecc2b73f035e711f18Timo Sirainen if (array_is_created(&ctx->cache_data_wanted_seqs))