bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
7d75b95c260e58ee5039fa3afd79e9fda3bc8002Timo Sirainenmail_cache_merge_bitmask(struct mail_cache_copy_context *ctx,
7d75b95c260e58ee5039fa3afd79e9fda3bc8002Timo Sirainen pos = array_idx_modifiable(&ctx->bitmask_pos, field->field_idx);
7d75b95c260e58ee5039fa3afd79e9fda3bc8002Timo Sirainen if (*pos == 0) {
7d75b95c260e58ee5039fa3afd79e9fda3bc8002Timo Sirainen /* we decided to drop this field */
7d75b95c260e58ee5039fa3afd79e9fda3bc8002Timo Sirainen dest = buffer_get_space_unsafe(ctx->buffer, *pos, field->size);
7d75b95c260e58ee5039fa3afd79e9fda3bc8002Timo Sirainen dest[i] |= ((const unsigned char*)field->data)[i];
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainenmail_cache_compress_field(struct mail_cache_copy_context *ctx,
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen file_field_idx = ctx->field_file_map[field->field_idx];
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen cache_field = &ctx->cache->fields[field->field_idx].field;
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen field_seen = buffer_get_space_unsafe(ctx->field_seen,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* duplicate */
7d75b95c260e58ee5039fa3afd79e9fda3bc8002Timo Sirainen if (cache_field->type == MAIL_CACHE_FIELD_BITMASK)
dd4b5f14b71b01a84af942e720a2d6e5f15ee1a7Timo Sirainen dec = cache_field->decision & ~MAIL_CACHE_DECISION_FORCED;
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen buffer_append(ctx->buffer, &file_field_idx, sizeof(file_field_idx));
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen buffer_append(ctx->buffer, &size32, sizeof(size32));
7d75b95c260e58ee5039fa3afd79e9fda3bc8002Timo Sirainen if (cache_field->type == MAIL_CACHE_FIELD_BITMASK) {
7d75b95c260e58ee5039fa3afd79e9fda3bc8002Timo Sirainen /* remember the position in case we need to update it */
7d75b95c260e58ee5039fa3afd79e9fda3bc8002Timo Sirainen array_idx_set(&ctx->bitmask_pos, field->field_idx, &pos);
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen buffer_append(ctx->buffer, field->data, field->size);
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen buffer_append_zero(ctx->buffer, 4 - (field->size & 3));
cfd0e35fce37786f1bb186386ebe29aeef7dbf31Timo Sirainenstatic uint32_t get_next_file_seq(struct mail_cache *cache)
cfd0e35fce37786f1bb186386ebe29aeef7dbf31Timo Sirainen /* make sure we look up the latest reset_id */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen ext = mail_index_view_get_ext(view, cache->ext_id);
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen file_seq = ext != NULL ? ext->reset_id + 1 : (uint32_t)ioloop_time;
6237f743bbaf74de5a2d2051672baed87023657bTimo Sirainen if (cache->hdr != NULL && file_seq <= cache->hdr->file_seq)
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainenmail_cache_compress_get_fields(struct mail_cache_copy_context *ctx,
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen /* Make mail_cache_header_fields_get() return the fields in
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen the same order as we saved them. */
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen memcpy(cache->field_file_map, ctx->field_file_map,
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen /* reverse mapping */
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen cache->file_field_map = used_fields_count == 0 ? NULL :
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen for (i = j = 0; i < cache->fields_count; i++) {
fd44baee6e92f13d42ff711895d6510067f70955Timo Sirainen /* change permanent decisions to temporary decisions.
fd44baee6e92f13d42ff711895d6510067f70955Timo Sirainen if they're still permanent they'll get updated later. */
fd44baee6e92f13d42ff711895d6510067f70955Timo Sirainen if (field->decision == MAIL_CACHE_DECISION_YES)
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen mail_cache_header_fields_get(cache, ctx->buffer);
7ca9da144f79317f9a0d3b0b5cc1fe21e44a5bf4Timo Sirainenmail_cache_copy(struct mail_cache *cache, struct mail_index_transaction *trans,
f883b315ca72073b58020798e6d907340b327228Timo Sirainen int fd, uint32_t *file_seq_r, uoff_t *file_size_r, uint32_t *max_uid_r,
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen uint32_t message_count, seq, first_new_seq, ext_offset;
f0ff961282e618945dfe997dc45ff95d656e5790Timo Sirainen unsigned int i, used_fields_count, orig_fields_count, record_count;
cfd0e35fce37786f1bb186386ebe29aeef7dbf31Timo Sirainen /* get the latest info on fields */
7ca9da144f79317f9a0d3b0b5cc1fe21e44a5bf4Timo Sirainen view = mail_index_transaction_get_view(trans);
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen cache_view = mail_cache_view_open(cache, view);
93fa87cf1a96c4f279ec4f5c311820313ba12c34Timo Sirainen output = o_stream_create_fd_file(fd, 0, FALSE);
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen ctx.buffer = buffer_create_dynamic(default_pool, 4096);
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen ctx.field_seen = buffer_create_dynamic(default_pool, 64);
b879ed8dd4b5850987e6b89a92f794d87c6be7d7Timo Sirainen ctx.field_file_map = t_new(uint32_t, cache->fields_count + 1);
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen /* @UNSAFE: drop unused fields and create a field mapping for
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen used fields */
67bbcd664bebce9a507a49c67273be4814d07c97Timo Sirainen cache->index->optimization_set.cache.unaccessed_field_drop_secs;
fe6cf42464c36ab281c0e0740f0255be77453670Timo Sirainen /* creating the initial cache file. add all fields. */
fe6cf42464c36ab281c0e0740f0255be77453670Timo Sirainen for (i = 0; i < orig_fields_count; i++)
fe6cf42464c36ab281c0e0740f0255be77453670Timo Sirainen for (i = used_fields_count = 0; i < orig_fields_count; i++) {
cc3ccdab8a510d88fecedf95187465bf84833711Timo Sirainen /* if the decision isn't forced and this field hasn't
cc3ccdab8a510d88fecedf95187465bf84833711Timo Sirainen been accessed for a while, drop it */
cc3ccdab8a510d88fecedf95187465bf84833711Timo Sirainen if ((dec & MAIL_CACHE_DECISION_FORCED) == 0 &&
ddbad7a661c0663fafd2b79393efa85f840d6af6Timo Sirainen /* drop all fields we don't want */
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen /* get sequence of first message which doesn't need its temp fields
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen first_new_seq = mail_cache_get_first_new_seq(view);
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen message_count = mail_index_view_get_messages_count(view);
f0ff961282e618945dfe997dc45ff95d656e5790Timo Sirainen i_array_init(ext_offsets, message_count); record_count = 0;
7ca9da144f79317f9a0d3b0b5cc1fe21e44a5bf4Timo Sirainen if (mail_index_transaction_is_expunged(trans, seq)) {
8d80659e504ffb34bb0c6a633184fece35751b18Timo Sirainen memset(buffer_get_modifiable_data(ctx.field_seen, NULL),
992a13add4eea0810e4db0f042a595dddf85536aTimo Sirainen buffer_append(ctx.buffer, &cache_rec, sizeof(cache_rec));
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen mail_cache_lookup_iter_init(cache_view, seq, &iter);
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen while (mail_cache_lookup_iter_next(&iter, &field) > 0)
67bbcd664bebce9a507a49c67273be4814d07c97Timo Sirainen ctx.buffer->used > cache->index->optimization_set.cache.record_max_size) {
7ca9da144f79317f9a0d3b0b5cc1fe21e44a5bf4Timo Sirainen /* nothing cached */
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, ctx.buffer->data, cache_rec.size);
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen i_assert(orig_fields_count == cache->fields_count);
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen hdr.field_header_offset = mail_index_uint32_to_offset(output->offset);
9a583c7a827f7a4d89ee43774f2d51ea6a214543Timo Sirainen mail_cache_compress_get_fields(&ctx, used_fields_count);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, ctx.buffer->data, ctx.buffer->used);
289064eb21595d3e4460439eccdc48232d13f5e1Timo Sirainen hdr.backwards_compat_used_file_size = output->offset;
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen mail_cache_set_syscall_error(cache, "write()");
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen if (cache->index->fsync_mode == FSYNC_MODE_ALWAYS) {
369a1084c500a9df7448ffa9409ce32e42060bc2Timo Sirainen mail_cache_set_syscall_error(cache, "fdatasync()");
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainenmail_cache_compress_write(struct mail_cache *cache,
f883b315ca72073b58020798e6d907340b327228Timo Sirainen if (mail_cache_copy(cache, trans, fd, &file_seq, &file_size,
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen mail_cache_set_syscall_error(cache, "fstat()");
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen mail_cache_set_syscall_error(cache, "rename()");
f883b315ca72073b58020798e6d907340b327228Timo Sirainen if ((cache->index->flags & MAIL_INDEX_OPEN_FLAG_DEBUG) != 0) {
f883b315ca72073b58020798e6d907340b327228Timo Sirainen i_debug("%s: Compressed, file_seq changed %u -> %u, "
f883b315ca72073b58020798e6d907340b327228Timo Sirainen "size=%"PRIuUOFF_T", max_uid=%u", cache->filepath,
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen /* once we're sure that the compression was successful,
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen update the offsets */
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen mail_index_ext_reset(trans, cache->ext_id, file_seq, TRUE);
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen for (i = 0; i < count; i++) {
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen if (offsets[i] != 0) {
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen mail_index_update_ext(trans, i + 1, cache->ext_id,
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainenstatic int mail_cache_compress_has_file_changed(struct mail_cache *cache)
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen unsigned int i;
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen for (i = 0;; i++) {
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen fd = nfs_safe_open(cache->filepath, O_RDONLY);
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen mail_cache_set_syscall_error(cache, "open()");
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen /* previously it didn't exist or it
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen was unusable and was just unlinked */
f0339f522dc9c8e2e8a29ef9a3f937c431c6bd1bTimo Sirainen return hdr.file_seq != cache->need_compress_file_seq ? 1 : 0;
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen } else if (errno != ESTALE || i >= NFS_ESTALE_RETRY_COUNT) {
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen mail_cache_set_syscall_error(cache, "read()");
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainenstatic int mail_cache_compress_dotlock(struct mail_cache *cache,
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen if (file_dotlock_create(&cache->dotlock_settings, cache->filepath,
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen DOTLOCK_CREATE_FLAG_NONBLOCK, dotlock_r) <= 0) {
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen mail_cache_set_syscall_error(cache, "file_dotlock_open()");
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainenstatic int mail_cache_compress_locked(struct mail_cache *cache,
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen /* There are two possible locking situations here:
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen a) Cache is locked against any modifications.
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen b) Cache doesn't exist or is unusable. There's no lock.
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen Because the cache lock itself is unreliable, we'll be using a
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen separate dotlock to guard against two processes compressing the
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen cache at the same time. */
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen if (mail_cache_compress_dotlock(cache, dotlock_r) < 0)
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen /* we've locked the cache compression now. if somebody else had just
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen recreated the cache, reopen the cache and return success. */
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen if ((ret = mail_cache_compress_has_file_changed(cache)) != 0) {
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen /* was just compressed, forget this */
5b47087754f4008e88c93386d524e07db535c132Timo Sirainen /* make sure we have mapped it before reading. */
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen /* we want to recreate the cache. write it first to a temporary file */
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen fd = mail_index_create_tmp_file(cache->index, cache->filepath, &temp_path);
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen if (mail_cache_compress_write(cache, trans, fd, temp_path, unlock) < 0) {
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen file_cache_set_fd(cache->file_cache, cache->fd);
7ca9da144f79317f9a0d3b0b5cc1fe21e44a5bf4Timo Sirainenint mail_cache_compress(struct mail_cache *cache,
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen if (MAIL_INDEX_IS_IN_MEMORY(cache->index) || cache->index->readonly) {
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen *lock_r = i_new(struct mail_cache_compress_lock, 1);
18d92dbbb752c79dc461514e52f7ef11847e636bTimo Sirainen /* compression isn't very efficient with small read()s */
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen if (cache->index->lock_method == FILE_LOCK_METHOD_DOTLOCK) {
365435901c22df7e5838f574c950b0e32e77f78aTimo Sirainen /* we're using dotlocking, cache file creation itself creates
365435901c22df7e5838f574c950b0e32e77f78aTimo Sirainen the dotlock file we need. */
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen /* already locked or some other error */
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen /* cache is broken or doesn't exist.
5724e7103eed12fe36b55a7b5a8653284a2184b9Timo Sirainen just start creating it. */
5724e7103eed12fe36b55a7b5a8653284a2184b9Timo Sirainen /* locking succeeded. */
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen ret = mail_cache_compress_locked(cache, trans, &unlock, &dotlock);
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen /* the fields may have been updated in memory already.
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen reverse those changes by re-reading them from file. */
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen *lock_r = i_new(struct mail_cache_compress_lock, 1);
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainenvoid mail_cache_compress_unlock(struct mail_cache_compress_lock **_lock)
0f833d18af36bf307cd69f235eb3fb779245b478Timo Sirainen struct mail_cache_compress_lock *lock = *_lock;
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenbool mail_cache_need_compress(struct mail_cache *cache)