1 /* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "ioloop.h"
5 #include "array.h"
6 #include "buffer.h"
7 #include "module-context.h"
8 #include "file-cache.h"
9 #include "file-set-size.h"
10 #include "read-full.h"
11 #include "write-full.h"
12 #include "mail-cache-private.h"
13 #include "mail-index-transaction-private.h"
14 
15 #include <stddef.h>
16 #include <sys/stat.h>
17 
18 #define MAIL_CACHE_INIT_WRITE_BUFFER (1024*16)
19 
20 #define CACHE_TRANS_CONTEXT(obj) \
21 	MODULE_CONTEXT(obj, cache_mail_index_transaction_module)
22 #define CACHE_TRANS_CONTEXT_REQUIRE(obj) \
23 	MODULE_CONTEXT_REQUIRE(obj, cache_mail_index_transaction_module)
24 
25 struct mail_cache_transaction_rec {
26 	uint32_t seq;
27 	uint32_t cache_data_pos;
28 };
29 
30 struct mail_cache_transaction_ctx {
31 	union mail_index_transaction_module_context module_ctx;
32 	struct mail_index_transaction_vfuncs super;
33 
34 	struct mail_cache *cache;
35 	struct mail_cache_view *view;
36 	struct mail_index_transaction *trans;
37 
38 	uint32_t cache_file_seq;
39 	uint32_t first_new_seq;
40 
41 	buffer_t *cache_data;
42 	ARRAY(uint8_t) cache_field_idx_used;
43 	ARRAY(struct mail_cache_transaction_rec) cache_data_seq;
44 	ARRAY_TYPE(seq_range) cache_data_wanted_seqs;
45 	uint32_t prev_seq, min_seq;
46 	size_t last_rec_pos;
47 
48 	unsigned int records_written;
49 
50 	bool tried_purging:1;
51 	bool decisions_refreshed:1;
52 	bool have_noncommited_mails:1;
53 	bool changes:1;
54 };
55 
56 static MODULE_CONTEXT_DEFINE_INIT(cache_mail_index_transaction_module,
57 				  &mail_index_module_register);
58 
59 static int mail_cache_transaction_lock(struct mail_cache_transaction_ctx *ctx);
60 static bool
61 mail_cache_transaction_update_last_rec_size(struct mail_cache_transaction_ctx *ctx,
62 					    size_t *size_r);
63 static int mail_cache_header_rewrite_fields(struct mail_cache *cache);
64 
mail_index_transaction_cache_reset(struct mail_index_transaction * t)65 static void mail_index_transaction_cache_reset(struct mail_index_transaction *t)
66 {
67 	struct mail_cache_transaction_ctx *ctx = CACHE_TRANS_CONTEXT_REQUIRE(t);
68 	struct mail_index_transaction_vfuncs super = ctx->super;
69 
70 	mail_cache_transaction_reset(ctx);
71 	super.reset(t);
72 }
73 
74 static int
mail_index_transaction_cache_commit(struct mail_index_transaction * t,struct mail_index_transaction_commit_result * result_r)75 mail_index_transaction_cache_commit(struct mail_index_transaction *t,
76 				    struct mail_index_transaction_commit_result *result_r)
77 {
78 	struct mail_cache_transaction_ctx *ctx = CACHE_TRANS_CONTEXT_REQUIRE(t);
79 	struct mail_index_transaction_vfuncs super = ctx->super;
80 
81 	/* a failed cache commit isn't important enough to fail the entire
82 	   index transaction, so we'll just ignore it */
83 	(void)mail_cache_transaction_commit(&ctx);
84 	return super.commit(t, result_r);
85 }
86 
87 static void
mail_index_transaction_cache_rollback(struct mail_index_transaction * t)88 mail_index_transaction_cache_rollback(struct mail_index_transaction *t)
89 {
90 	struct mail_cache_transaction_ctx *ctx = CACHE_TRANS_CONTEXT_REQUIRE(t);
91 	struct mail_index_transaction_vfuncs super = ctx->super;
92 
93 	mail_cache_transaction_rollback(&ctx);
94 	super.rollback(t);
95 }
96 
97 struct mail_cache_transaction_ctx *
mail_cache_get_transaction(struct mail_cache_view * view,struct mail_index_transaction * t)98 mail_cache_get_transaction(struct mail_cache_view *view,
99 			   struct mail_index_transaction *t)
100 {
101 	struct mail_cache_transaction_ctx *ctx;
102 
103 	ctx = !cache_mail_index_transaction_module.id.module_id_set ? NULL :
104 		CACHE_TRANS_CONTEXT(t);
105 
106 	if (ctx != NULL)
107 		return ctx;
108 
109 	ctx = i_new(struct mail_cache_transaction_ctx, 1);
110 	ctx->cache = view->cache;
111 	ctx->view = view;
112 	ctx->trans = t;
113 
114 	i_assert(view->transaction == NULL);
115 	view->transaction = ctx;
116 	view->trans_view = mail_index_transaction_open_updated_view(t);
117 
118 	ctx->super = t->v;
119 	t->v.reset = mail_index_transaction_cache_reset;
120 	t->v.commit = mail_index_transaction_cache_commit;
121 	t->v.rollback = mail_index_transaction_cache_rollback;
122 
123 	MODULE_CONTEXT_SET(t, cache_mail_index_transaction_module, ctx);
124 	return ctx;
125 }
126 
127 static void
mail_cache_transaction_forget_flushed(struct mail_cache_transaction_ctx * ctx,bool reset_id_changed)128 mail_cache_transaction_forget_flushed(struct mail_cache_transaction_ctx *ctx,
129 				      bool reset_id_changed)
130 {
131 	uint32_t new_cache_file_seq = MAIL_CACHE_IS_UNUSABLE(ctx->cache) ? 0 :
132 		ctx->cache->hdr->file_seq;
133 	if (reset_id_changed && ctx->records_written > 0) {
134 		e_warning(ctx->cache->event,
135 			  "Purging lost %u written cache records "
136 			  "(reset_id changed %u -> %u)", ctx->records_written,
137 			  ctx->cache_file_seq, new_cache_file_seq);
138 		/* don't increase deleted_record_count in the new file */
139 		ctx->records_written = 0;
140 	}
141 	ctx->cache_file_seq = new_cache_file_seq;
142 	/* forget all cache extension updates even if reset_id doesn't change */
143 	mail_index_ext_set_reset_id(ctx->trans, ctx->cache->ext_id,
144 				    ctx->cache_file_seq);
145 }
146 
mail_cache_transaction_reset(struct mail_cache_transaction_ctx * ctx)147 void mail_cache_transaction_reset(struct mail_cache_transaction_ctx *ctx)
148 {
149 	mail_cache_transaction_forget_flushed(ctx, FALSE);
150 	if (ctx->cache_data != NULL)
151 		buffer_set_used_size(ctx->cache_data, 0);
152 	if (array_is_created(&ctx->cache_data_seq))
153 		array_clear(&ctx->cache_data_seq);
154 	ctx->prev_seq = 0;
155 	ctx->last_rec_pos = 0;
156 
157 	ctx->changes = FALSE;
158 }
159 
mail_cache_transaction_rollback(struct mail_cache_transaction_ctx ** _ctx)160 void mail_cache_transaction_rollback(struct mail_cache_transaction_ctx **_ctx)
161 {
162 	struct mail_cache_transaction_ctx *ctx = *_ctx;
163 
164 	*_ctx = NULL;
165 
166 	if (ctx->records_written > 0) {
167 		/* we already wrote to the cache file. we can't (or don't want
168 		   to) delete that data, so just mark it as deleted space */
169 		if (mail_cache_transaction_lock(ctx) > 0) {
170 			ctx->cache->hdr_copy.deleted_record_count +=
171 				ctx->records_written;
172 			ctx->cache->hdr_modified = TRUE;
173 			(void)mail_cache_flush_and_unlock(ctx->cache);
174 		}
175 	}
176 
177 	MODULE_CONTEXT_UNSET(ctx->trans, cache_mail_index_transaction_module);
178 
179 	ctx->view->transaction = NULL;
180 	ctx->view->trans_seq1 = ctx->view->trans_seq2 = 0;
181 
182 	mail_index_view_close(&ctx->view->trans_view);
183 	buffer_free(&ctx->cache_data);
184 	if (array_is_created(&ctx->cache_data_seq))
185 		array_free(&ctx->cache_data_seq);
186 	if (array_is_created(&ctx->cache_data_wanted_seqs))
187 		array_free(&ctx->cache_data_wanted_seqs);
188 	array_free(&ctx->cache_field_idx_used);
189 	i_free(ctx);
190 }
191 
mail_cache_transactions_have_changes(struct mail_cache * cache)192 bool mail_cache_transactions_have_changes(struct mail_cache *cache)
193 {
194 	struct mail_cache_view *view;
195 
196 	for (view = cache->views; view != NULL; view = view->next) {
197 		if (view->transaction != NULL &&
198 		    view->transaction->changes)
199 			return TRUE;
200 	}
201 	return FALSE;
202 }
203 
204 static int
mail_cache_transaction_purge(struct mail_cache_transaction_ctx * ctx,const char * reason)205 mail_cache_transaction_purge(struct mail_cache_transaction_ctx *ctx,
206 			     const char *reason)
207 {
208 	struct mail_cache *cache = ctx->cache;
209 
210 	ctx->tried_purging = TRUE;
211 
212 	uint32_t purge_file_seq =
213 		MAIL_CACHE_IS_UNUSABLE(cache) ? 0 : cache->hdr->file_seq;
214 
215 	int ret = mail_cache_purge(cache, purge_file_seq, reason);
216 	/* already written cache records must be forgotten, but records in
217 	   memory can still be written to the new cache file */
218 	mail_cache_transaction_forget_flushed(ctx, TRUE);
219 	return ret;
220 }
221 
mail_cache_transaction_lock(struct mail_cache_transaction_ctx * ctx)222 static int mail_cache_transaction_lock(struct mail_cache_transaction_ctx *ctx)
223 {
224 	struct mail_cache *cache = ctx->cache;
225 	const uoff_t cache_max_size =
226 		cache->index->optimization_set.cache.max_size;
227 	int ret;
228 
229 	if ((ret = mail_cache_lock(cache)) <= 0) {
230 		if (ret < 0)
231 			return -1;
232 
233 		if (!ctx->tried_purging) {
234 			if (mail_cache_transaction_purge(ctx, "creating cache") < 0)
235 				return -1;
236 			return mail_cache_transaction_lock(ctx);
237 		} else {
238 			return 0;
239 		}
240 	}
241 	i_assert(!MAIL_CACHE_IS_UNUSABLE(cache));
242 
243 	if (!ctx->tried_purging && ctx->cache_data != NULL &&
244 	    cache->last_stat_size + ctx->cache_data->used > cache_max_size) {
245 		/* Looks like cache file is becoming too large. Try to purge
246 		   it to free up some space. */
247 		if (cache->hdr->continued_record_count > 0 ||
248 		    cache->hdr->deleted_record_count > 0) {
249 			mail_cache_unlock(cache);
250 			(void)mail_cache_transaction_purge(ctx, "cache is too large");
251 			return mail_cache_transaction_lock(ctx);
252 		}
253 	}
254 
255 	if (ctx->cache_file_seq == 0)
256 		ctx->cache_file_seq = cache->hdr->file_seq;
257 	else if (ctx->cache_file_seq != cache->hdr->file_seq) {
258 		/* already written cache records must be forgotten, but records
259 		   in memory can still be written to the new cache file */
260 		mail_cache_transaction_forget_flushed(ctx, TRUE);
261 		i_assert(ctx->cache_file_seq == cache->hdr->file_seq);
262 	}
263 	return 1;
264 }
265 
266 const struct mail_cache_record *
mail_cache_transaction_lookup_rec(struct mail_cache_transaction_ctx * ctx,unsigned int seq,unsigned int * trans_next_idx)267 mail_cache_transaction_lookup_rec(struct mail_cache_transaction_ctx *ctx,
268 				  unsigned int seq,
269 				  unsigned int *trans_next_idx)
270 {
271 	const struct mail_cache_transaction_rec *recs;
272 	unsigned int i, count;
273 
274 	recs = array_get(&ctx->cache_data_seq, &count);
275 	for (i = *trans_next_idx; i < count; i++) {
276 		if (recs[i].seq == seq) {
277 			*trans_next_idx = i + 1;
278 			return CONST_PTR_OFFSET(ctx->cache_data->data,
279 						recs[i].cache_data_pos);
280 		}
281 	}
282 	*trans_next_idx = i + 1;
283 	if (seq == ctx->prev_seq && i == count) {
284 		/* update the unfinished record's (temporary) size and
285 		   return it */
286 		size_t size;
287 		if (!mail_cache_transaction_update_last_rec_size(ctx, &size))
288 			return NULL;
289 		return CONST_PTR_OFFSET(ctx->cache_data->data,
290 					ctx->last_rec_pos);
291 	}
292 	return NULL;
293 }
294 
295 static void
mail_cache_transaction_update_index(struct mail_cache_transaction_ctx * ctx,uint32_t write_offset,bool committing)296 mail_cache_transaction_update_index(struct mail_cache_transaction_ctx *ctx,
297 				    uint32_t write_offset, bool committing)
298 {
299 	struct mail_cache *cache = ctx->cache;
300 	struct mail_index_transaction *trans;
301 	const struct mail_cache_record *rec = ctx->cache_data->data;
302 	const struct mail_cache_transaction_rec *recs;
303 	uint32_t i, seq_count;
304 
305 	if (committing) {
306 		/* The transaction is being committed now. Use it. */
307 		trans = ctx->trans;
308 	} else if (ctx->have_noncommited_mails) {
309 		/* Some of the mails haven't been committed yet. We must use
310 		   the provided transaction to update the cache records. */
311 		trans = ctx->trans;
312 	} else {
313 		/* We can commit these changes immediately. This way even if
314 		   the provided transaction runs for a very long time, we
315 		   still once in a while commit the cache changes so they
316 		   become visible to other processes as well. */
317 		trans = mail_index_transaction_begin(ctx->view->trans_view,
318 			MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL);
319 	}
320 
321 	mail_index_ext_using_reset_id(trans, ctx->cache->ext_id,
322 				      ctx->cache_file_seq);
323 
324 	/* write the cache_offsets to index file. records' prev_offset
325 	   is updated to point to old cache record when index is being
326 	   synced. */
327 	recs = array_get(&ctx->cache_data_seq, &seq_count);
328 	for (i = 0; i < seq_count; i++) {
329 		mail_index_update_ext(trans, recs[i].seq, cache->ext_id,
330 				      &write_offset, NULL);
331 
332 		write_offset += rec->size;
333 		rec = CONST_PTR_OFFSET(rec, rec->size);
334 		ctx->records_written++;
335 	}
336 	if (trans != ctx->trans) {
337 		i_assert(cache->index->log_sync_locked);
338 		if (mail_index_transaction_commit(&trans) < 0) {
339 			/* failed, but can't really do anything */
340 		} else {
341 			ctx->records_written = 0;
342 		}
343 	}
344 }
345 
346 static int
mail_cache_link_records(struct mail_cache_transaction_ctx * ctx,uint32_t write_offset)347 mail_cache_link_records(struct mail_cache_transaction_ctx *ctx,
348 			uint32_t write_offset)
349 {
350 	struct mail_index_map *map;
351 	struct mail_cache_record *rec;
352 	const struct mail_cache_transaction_rec *recs;
353 	const uint32_t *prev_offsetp;
354 	ARRAY_TYPE(uint32_t) seq_offsets;
355 	uint32_t i, seq_count, reset_id, prev_offset, *offsetp;
356 	const void *data;
357 
358 	i_assert(ctx->min_seq != 0);
359 
360 	i_array_init(&seq_offsets, 64);
361 	recs = array_get(&ctx->cache_data_seq, &seq_count);
362 	rec = buffer_get_modifiable_data(ctx->cache_data, NULL);
363 	for (i = 0; i < seq_count; i++) {
364 		offsetp = array_idx_get_space(&seq_offsets,
365 					       recs[i].seq - ctx->min_seq);
366 		if (*offsetp != 0)
367 			prev_offset = *offsetp;
368 		else {
369 			mail_index_lookup_ext_full(ctx->view->trans_view, recs[i].seq,
370 						   ctx->cache->ext_id, &map,
371 						   &data, NULL);
372 			prev_offsetp = data;
373 
374 			if (prev_offsetp == NULL || *prev_offsetp == 0)
375 				prev_offset = 0;
376 			else if (mail_index_ext_get_reset_id(ctx->view->trans_view, map,
377 							     ctx->cache->ext_id,
378 							     &reset_id) &&
379 				 reset_id == ctx->cache_file_seq)
380 				prev_offset = *prev_offsetp;
381 			else
382 				prev_offset = 0;
383 			if (prev_offset >= write_offset) {
384 				mail_cache_set_corrupted(ctx->cache,
385 					"Cache record offset points outside existing file");
386 				array_free(&seq_offsets);
387 				return -1;
388 			}
389 		}
390 
391 		if (prev_offset != 0) {
392 			/* link this record to previous one */
393 			rec->prev_offset = prev_offset;
394 			ctx->cache->hdr_copy.continued_record_count++;
395 		} else {
396 			ctx->cache->hdr_copy.record_count++;
397 		}
398 		*offsetp = write_offset;
399 
400 		write_offset += rec->size;
401 		rec = PTR_OFFSET(rec, rec->size);
402 	}
403 	array_free(&seq_offsets);
404 	ctx->cache->hdr_modified = TRUE;
405 	return 0;
406 }
407 
408 static bool
mail_cache_transaction_set_used(struct mail_cache_transaction_ctx * ctx)409 mail_cache_transaction_set_used(struct mail_cache_transaction_ctx *ctx)
410 {
411 	const uint8_t *cache_fields_used;
412 	unsigned int field_idx, count;
413 	bool missing_file_fields = FALSE;
414 
415 	cache_fields_used = array_get(&ctx->cache_field_idx_used, &count);
416 	i_assert(count <= ctx->cache->fields_count);
417 	for (field_idx = 0; field_idx < count; field_idx++) {
418 		if (cache_fields_used[field_idx] != 0) {
419 			ctx->cache->fields[field_idx].used = TRUE;
420 			if (ctx->cache->field_file_map[field_idx] == (uint32_t)-1)
421 				missing_file_fields = TRUE;
422 		}
423 	}
424 	return missing_file_fields;
425 }
426 
427 static int
mail_cache_transaction_update_fields(struct mail_cache_transaction_ctx * ctx)428 mail_cache_transaction_update_fields(struct mail_cache_transaction_ctx *ctx)
429 {
430 	unsigned char *p;
431 	const unsigned char *end, *rec_end;
432 	uint32_t field_idx, data_size;
433 
434 	if (mail_cache_transaction_set_used(ctx)) {
435 		/* add missing fields to cache */
436 		if (mail_cache_header_rewrite_fields(ctx->cache) < 0)
437 			return -1;
438 		/* make sure they were actually added */
439 		if (mail_cache_transaction_set_used(ctx)) {
440 			mail_index_set_error(ctx->cache->index,
441 				"Cache file %s: Unexpectedly lost newly added field",
442 				ctx->cache->filepath);
443 			return -1;
444 		}
445 	}
446 
447 	/* Go through all the added cache records and replace the in-memory
448 	   field_idx with the cache file-specific field index. Update only
449 	   up to last_rec_pos, because that's how far flushing is done. The
450 	   fields after that keep the in-memory field_idx until the next
451 	   flush. */
452 	p = buffer_get_modifiable_data(ctx->cache_data, NULL);
453 	end = CONST_PTR_OFFSET(ctx->cache_data->data, ctx->last_rec_pos);
454 	rec_end = p;
455 	while (p < end) {
456 		if (p >= rec_end) {
457 			/* next cache record */
458 			i_assert(p == rec_end);
459 			const struct mail_cache_record *rec =
460 				(const struct mail_cache_record *)p;
461 			/* note that the last rec->size==0 */
462 			rec_end = CONST_PTR_OFFSET(p, rec->size);
463 			p += sizeof(*rec);
464 		}
465 		/* replace field_idx */
466 		uint32_t *file_fieldp = (uint32_t *)p;
467 		field_idx = *file_fieldp;
468 		*file_fieldp = ctx->cache->field_file_map[field_idx];
469 		i_assert(*file_fieldp != (uint32_t)-1);
470 		p += sizeof(field_idx);
471 
472 		/* Skip to next cache field. Next is <data size> if the field
473 		   is not fixed size. */
474 		data_size = ctx->cache->fields[field_idx].field.field_size;
475 		if (data_size == UINT_MAX) {
476 			memcpy(&data_size, p, sizeof(data_size));
477 			p += sizeof(data_size);
478 		}
479 		/* data & 32bit padding */
480 		p += data_size;
481 		if ((data_size & 3) != 0)
482 			p += 4 - (data_size & 3);
483 	}
484 	i_assert(p == end);
485 	return 0;
486 }
487 
488 static void
mail_cache_transaction_drop_last_flush(struct mail_cache_transaction_ctx * ctx)489 mail_cache_transaction_drop_last_flush(struct mail_cache_transaction_ctx *ctx)
490 {
491 	buffer_copy(ctx->cache_data, 0,
492 		    ctx->cache_data, ctx->last_rec_pos, SIZE_MAX);
493 	buffer_set_used_size(ctx->cache_data,
494 			     ctx->cache_data->used - ctx->last_rec_pos);
495 	ctx->last_rec_pos = 0;
496 	ctx->min_seq = 0;
497 
498 	array_clear(&ctx->cache_data_seq);
499 	array_clear(&ctx->cache_data_wanted_seqs);
500 }
501 
502 static int
mail_cache_transaction_flush(struct mail_cache_transaction_ctx * ctx,bool committing)503 mail_cache_transaction_flush(struct mail_cache_transaction_ctx *ctx,
504 			     bool committing)
505 {
506 	struct stat st;
507 	uint32_t write_offset = 0;
508 	int ret = 0;
509 
510 	i_assert(!ctx->cache->locked);
511 
512 	if (array_count(&ctx->cache_data_seq) == 0) {
513 		/* we had done some changes, but they were aborted. */
514 		i_assert(ctx->last_rec_pos == 0);
515 		ctx->min_seq = 0;
516 		return 0;
517 	}
518 
519 	/* If we're going to be committing a transaction, the log must be
520 	   locked before we lock cache or we can deadlock. */
521 	bool lock_log = !ctx->cache->index->log_sync_locked &&
522 		!committing && !ctx->have_noncommited_mails;
523 	if (lock_log) {
524 		uint32_t file_seq;
525 		uoff_t file_offset;
526 
527 		if (mail_transaction_log_sync_lock(ctx->cache->index->log,
528 				"mail cache transaction flush",
529 				&file_seq, &file_offset) < 0)
530 			return -1;
531 	}
532 
533 	if (mail_cache_transaction_lock(ctx) <= 0) {
534 		if (lock_log) {
535 			mail_transaction_log_sync_unlock(ctx->cache->index->log,
536 				"mail cache transaction flush: cache lock failed");
537 		}
538 		return -1;
539 	}
540 
541 	i_assert(ctx->cache_data != NULL);
542 	i_assert(ctx->last_rec_pos <= ctx->cache_data->used);
543 
544 	if (mail_cache_transaction_update_fields(ctx) < 0) {
545 		if (lock_log) {
546 			mail_transaction_log_sync_unlock(ctx->cache->index->log,
547 				"mail cache transaction flush: field update failed");
548 		}
549 		mail_cache_unlock(ctx->cache);
550 		return -1;
551 	}
552 
553 	/* we need to get the final write offset for linking records */
554 	if (fstat(ctx->cache->fd, &st) < 0) {
555 		if (!ESTALE_FSTAT(errno))
556 			mail_cache_set_syscall_error(ctx->cache, "fstat()");
557 		ret = -1;
558 	} else if ((uoff_t)st.st_size + ctx->last_rec_pos > ctx->cache->index->optimization_set.cache.max_size) {
559 		mail_cache_set_corrupted(ctx->cache, "Cache file too large");
560 		ret = -1;
561 	} else {
562 		write_offset = st.st_size;
563 		if (mail_cache_link_records(ctx, write_offset) < 0)
564 			ret = -1;
565 	}
566 
567 	/* write to cache file */
568 	if (ret < 0 ||
569 	    mail_cache_append(ctx->cache, ctx->cache_data->data,
570 			      ctx->last_rec_pos, &write_offset) < 0)
571 		ret = -1;
572 	else {
573 		/* update records' cache offsets to index */
574 		mail_cache_transaction_update_index(ctx, write_offset,
575 						    committing);
576 	}
577 	if (mail_cache_flush_and_unlock(ctx->cache) < 0)
578 		ret = -1;
579 
580 	if (lock_log) {
581 		mail_transaction_log_sync_unlock(ctx->cache->index->log,
582 			"mail cache transaction flush");
583 	}
584 	return ret;
585 }
586 
587 static void
mail_cache_transaction_drop_unwanted(struct mail_cache_transaction_ctx * ctx,size_t space_needed)588 mail_cache_transaction_drop_unwanted(struct mail_cache_transaction_ctx *ctx,
589 				     size_t space_needed)
590 {
591 	struct mail_cache_transaction_rec *recs;
592 	unsigned int i, count;
593 
594 	recs = array_get_modifiable(&ctx->cache_data_seq, &count);
595 	/* find out how many records to delete. delete all unwanted sequences,
596 	   and if that's not enough delete some more. */
597 	for (i = 0; i < count; i++) {
598 		if (seq_range_exists(&ctx->cache_data_wanted_seqs, recs[i].seq)) {
599 			if (recs[i].cache_data_pos >= space_needed)
600 				break;
601 			/* we're going to forcibly delete it - remove it also
602 			   from the array since it's no longer useful there */
603 			seq_range_array_remove(&ctx->cache_data_wanted_seqs,
604 					       recs[i].seq);
605 		}
606 	}
607 	unsigned int deleted_count = i;
608 	size_t deleted_space = i < count ?
609 		recs[i].cache_data_pos : ctx->last_rec_pos;
610 	for (; i < count; i++)
611 		recs[i].cache_data_pos -= deleted_space;
612 	ctx->last_rec_pos -= deleted_space;
613 	array_delete(&ctx->cache_data_seq, 0, deleted_count);
614 	buffer_delete(ctx->cache_data, 0, deleted_space);
615 }
616 
617 static bool
mail_cache_transaction_update_last_rec_size(struct mail_cache_transaction_ctx * ctx,size_t * size_r)618 mail_cache_transaction_update_last_rec_size(struct mail_cache_transaction_ctx *ctx,
619 					    size_t *size_r)
620 {
621 	struct mail_cache_record *rec;
622 	void *data;
623 	size_t size;
624 
625 	data = buffer_get_modifiable_data(ctx->cache_data, &size);
626 	rec = PTR_OFFSET(data, ctx->last_rec_pos);
627 	rec->size = size - ctx->last_rec_pos;
628 	if (rec->size == sizeof(*rec))
629 		return FALSE;
630 	i_assert(rec->size > sizeof(*rec));
631 	*size_r = rec->size;
632 	return TRUE;
633 }
634 
635 static void
mail_cache_transaction_update_last_rec(struct mail_cache_transaction_ctx * ctx)636 mail_cache_transaction_update_last_rec(struct mail_cache_transaction_ctx *ctx)
637 {
638 	struct mail_cache_transaction_rec *trans_rec;
639 	size_t size;
640 
641 	if (!mail_cache_transaction_update_last_rec_size(ctx, &size) ||
642 	    size > ctx->cache->index->optimization_set.cache.record_max_size) {
643 		buffer_set_used_size(ctx->cache_data, ctx->last_rec_pos);
644 		return;
645 	}
646 
647 	if (ctx->min_seq > ctx->prev_seq || ctx->min_seq == 0)
648 		ctx->min_seq = ctx->prev_seq;
649 	trans_rec = array_append_space(&ctx->cache_data_seq);
650 	trans_rec->seq = ctx->prev_seq;
651 	trans_rec->cache_data_pos = ctx->last_rec_pos;
652 	ctx->last_rec_pos = ctx->cache_data->used;
653 }
654 
655 static void
mail_cache_transaction_switch_seq(struct mail_cache_transaction_ctx * ctx)656 mail_cache_transaction_switch_seq(struct mail_cache_transaction_ctx *ctx)
657 {
658 	struct mail_cache_record new_rec;
659 
660 	if (ctx->prev_seq != 0) {
661 		/* update previously added cache record's size */
662 		mail_cache_transaction_update_last_rec(ctx);
663 	} else if (ctx->cache_data == NULL) {
664 		ctx->cache_data =
665 			buffer_create_dynamic(default_pool,
666 					      MAIL_CACHE_INIT_WRITE_BUFFER);
667 		i_array_init(&ctx->cache_data_seq, 64);
668 		i_array_init(&ctx->cache_data_wanted_seqs, 32);
669 		i_array_init(&ctx->cache_field_idx_used, 64);
670 	}
671 
672 	i_zero(&new_rec);
673 	buffer_append(ctx->cache_data, &new_rec, sizeof(new_rec));
674 
675 	ctx->prev_seq = 0;
676 	ctx->changes = TRUE;
677 }
678 
mail_cache_transaction_commit(struct mail_cache_transaction_ctx ** _ctx)679 int mail_cache_transaction_commit(struct mail_cache_transaction_ctx **_ctx)
680 {
681 	struct mail_cache_transaction_ctx *ctx = *_ctx;
682 	int ret = 0;
683 
684 	if (ctx->changes) {
685 		if (ctx->prev_seq != 0)
686 			mail_cache_transaction_update_last_rec(ctx);
687 		if (mail_cache_transaction_flush(ctx, TRUE) < 0)
688 			ret = -1;
689 		else {
690 			/* successfully wrote everything */
691 			ctx->records_written = 0;
692 		}
693 		/* Here would be a good place to do fdatasync() to make sure
694 		   everything is written before offsets are updated to index.
695 		   However it slows down I/O needlessly and we're pretty good
696 		   at catching and fixing cache corruption, so we no longer do
697 		   it. */
698 	}
699 	mail_cache_transaction_rollback(_ctx);
700 	return ret;
701 }
702 
703 static int
mail_cache_header_fields_write(struct mail_cache * cache,const buffer_t * buffer)704 mail_cache_header_fields_write(struct mail_cache *cache, const buffer_t *buffer)
705 {
706 	uint32_t offset, hdr_offset;
707 
708 	i_assert(cache->locked);
709 
710 	offset = 0;
711 	if (mail_cache_append(cache, buffer->data, buffer->used, &offset) < 0)
712 		return -1;
713 
714 	if (cache->index->set.fsync_mode == FSYNC_MODE_ALWAYS) {
715 		if (fdatasync(cache->fd) < 0) {
716 			mail_cache_set_syscall_error(cache, "fdatasync()");
717 			return -1;
718 		}
719 	}
720 	/* find offset to the previous header's "next_offset" field */
721 	if (mail_cache_header_fields_get_next_offset(cache, &hdr_offset) < 0)
722 		return -1;
723 
724 	/* update the next_offset offset, so our new header will be found */
725 	offset = mail_index_uint32_to_offset(offset);
726 	if (mail_cache_write(cache, &offset, sizeof(offset), hdr_offset) < 0)
727 		return -1;
728 
729 	if (hdr_offset == offsetof(struct mail_cache_header,
730 				   field_header_offset)) {
731 		/* we're adding the first field. hdr_copy needs to be kept
732 		   in sync so unlocking won't overwrite it. */
733 		cache->hdr_copy.field_header_offset = hdr_offset;
734 		cache->hdr_ro_copy.field_header_offset = hdr_offset;
735 	}
736 	return 0;
737 }
738 
mail_cache_header_rewrite_fields(struct mail_cache * cache)739 static int mail_cache_header_rewrite_fields(struct mail_cache *cache)
740 {
741 	int ret;
742 
743 	/* re-read header to make sure we don't lose any fields. */
744 	if (mail_cache_header_fields_read(cache) < 0)
745 		return -1;
746 
747 	T_BEGIN {
748 		buffer_t *buffer;
749 
750 		buffer = t_buffer_create(256);
751 		mail_cache_header_fields_get(cache, buffer);
752 		ret = mail_cache_header_fields_write(cache, buffer);
753 	} T_END;
754 
755 	if (ret == 0) {
756 		/* we wrote all the headers, so there are no pending changes */
757 		cache->field_header_write_pending = FALSE;
758 		ret = mail_cache_header_fields_read(cache);
759 	}
760 	return ret;
761 }
762 
763 static void
mail_cache_transaction_refresh_decisions(struct mail_cache_transaction_ctx * ctx)764 mail_cache_transaction_refresh_decisions(struct mail_cache_transaction_ctx *ctx)
765 {
766 	if (ctx->decisions_refreshed)
767 		return;
768 
769 	/* Read latest caching decisions from the cache file's header once
770 	   per transaction. */
771 	if (!ctx->cache->opened)
772 		(void)mail_cache_open_and_verify(ctx->cache);
773 	else
774 		(void)mail_cache_header_fields_read(ctx->cache);
775 	ctx->decisions_refreshed = TRUE;
776 }
777 
mail_cache_add(struct mail_cache_transaction_ctx * ctx,uint32_t seq,unsigned int field_idx,const void * data,size_t data_size)778 void mail_cache_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq,
779 		    unsigned int field_idx, const void *data, size_t data_size)
780 {
781 	uint32_t data_size32;
782 	unsigned int fixed_size;
783 	size_t full_size, record_size;
784 
785 	i_assert(field_idx < ctx->cache->fields_count);
786 	i_assert(data_size < (uint32_t)-1);
787 
788 	if (ctx->cache->fields[field_idx].field.decision ==
789 	    (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED))
790 		return;
791 
792 	if (seq >= ctx->trans->first_new_seq)
793 		ctx->have_noncommited_mails = TRUE;
794 
795 	/* If the cache file exists, make sure the caching decisions have been
796 	   read. */
797 	mail_cache_transaction_refresh_decisions(ctx);
798 
799 	mail_cache_decision_add(ctx->view, seq, field_idx);
800 
801 	fixed_size = ctx->cache->fields[field_idx].field.field_size;
802 	i_assert(fixed_size == UINT_MAX || fixed_size == data_size);
803 
804 	data_size32 = (uint32_t)data_size;
805 	full_size = sizeof(field_idx) + ((data_size + 3) & ~3U);
806 	if (fixed_size == UINT_MAX)
807 		full_size += sizeof(data_size32);
808 
809 	if (ctx->prev_seq != seq) {
810 		mail_cache_transaction_switch_seq(ctx);
811 		ctx->prev_seq = seq;
812 		seq_range_array_add(&ctx->cache_data_wanted_seqs, seq);
813 
814 		/* remember roughly what we have modified, so cache lookups can
815 		   look into transactions to see changes. */
816 		if (seq < ctx->view->trans_seq1 || ctx->view->trans_seq1 == 0)
817 			ctx->view->trans_seq1 = seq;
818 		if (seq > ctx->view->trans_seq2)
819 			ctx->view->trans_seq2 = seq;
820 	}
821 
822 	if (mail_cache_transaction_update_last_rec_size(ctx, &record_size) &&
823 	    record_size + full_size >
824 	    ctx->cache->index->optimization_set.cache.record_max_size) {
825 		/* Adding this field would exceed the cache record's maximum
826 		   size. If we don't add this, it's possible that other fields
827 		   could still be added. */
828 		return;
829 	}
830 
831 	/* Remember that this field has been used within the transaction. Later
832 	   on we fill mail_cache_field_private.used with it. We can't rely on
833 	   setting it here, because cache purging may run and clear it. */
834 	uint8_t field_idx_set = 1;
835 	array_idx_set(&ctx->cache_field_idx_used, field_idx, &field_idx_set);
836 
837 	/* Remember that this value exists for the mail, in case we try to look
838 	   it up. Note that this gets forgotten whenever changing the mail. */
839 	buffer_write(ctx->view->cached_exists_buf, field_idx,
840 		     &ctx->view->cached_exists_value, 1);
841 
842 	if (ctx->cache_data->used + full_size > MAIL_CACHE_MAX_WRITE_BUFFER &&
843 	    ctx->last_rec_pos > 0) {
844 		/* time to flush our buffer. */
845 		if (MAIL_INDEX_IS_IN_MEMORY(ctx->cache->index)) {
846 			/* just drop the old data to free up memory */
847 			size_t space_needed = ctx->cache_data->used +
848 				full_size - MAIL_CACHE_MAX_WRITE_BUFFER;
849 			mail_cache_transaction_drop_unwanted(ctx, space_needed);
850 		} else {
851 			if (mail_cache_transaction_flush(ctx, FALSE) < 0) {
852 				/* If this is a syscall failure, the already
853 				   flushed changes could still be finished by
854 				   writing the offsets to .log file. If this is
855 				   a corruption/lost cache, the offsets will
856 				   point to a nonexistent file or be ignored.
857 				   Either way, we don't really need to handle
858 				   this failure in any special way. */
859 			}
860 			/* Regardless of whether the flush succeeded, drop all
861 			   data that it would have written. This way the flush
862 			   is attempted only once, but it could still be
863 			   possible to write new data later. Also don't reset
864 			   the transaction entirely so that the last partially
865 			   cached mail can still be accessed from memory. */
866 			mail_cache_transaction_drop_last_flush(ctx);
867 		}
868 	}
869 
870 	buffer_append(ctx->cache_data, &field_idx, sizeof(field_idx));
871 	if (fixed_size == UINT_MAX) {
872 		buffer_append(ctx->cache_data, &data_size32,
873 			      sizeof(data_size32));
874 	}
875 
876 	buffer_append(ctx->cache_data, data, data_size);
877 	if ((data_size & 3) != 0)
878                 buffer_append_zero(ctx->cache_data, 4 - (data_size & 3));
879 }
880 
mail_cache_field_want_add(struct mail_cache_transaction_ctx * ctx,uint32_t seq,unsigned int field_idx)881 bool mail_cache_field_want_add(struct mail_cache_transaction_ctx *ctx,
882 			       uint32_t seq, unsigned int field_idx)
883 {
884 	enum mail_cache_decision_type decision;
885 
886 	mail_cache_transaction_refresh_decisions(ctx);
887 
888 	decision = mail_cache_field_get_decision(ctx->view->cache, field_idx);
889 	decision &= ENUM_NEGATE(MAIL_CACHE_DECISION_FORCED);
890 	switch (decision) {
891 	case MAIL_CACHE_DECISION_NO:
892 		return FALSE;
893 	case MAIL_CACHE_DECISION_TEMP:
894 		/* add it only if it's newer than what we would drop when
895 		   purging */
896 		if (ctx->first_new_seq == 0) {
897 			ctx->first_new_seq =
898 				mail_cache_get_first_new_seq(ctx->view->view);
899 		}
900 		if (seq < ctx->first_new_seq)
901 			return FALSE;
902 		break;
903 	default:
904 		break;
905 	}
906 
907 	return mail_cache_field_exists(ctx->view, seq, field_idx) == 0;
908 }
909 
mail_cache_field_can_add(struct mail_cache_transaction_ctx * ctx,uint32_t seq,unsigned int field_idx)910 bool mail_cache_field_can_add(struct mail_cache_transaction_ctx *ctx,
911 			      uint32_t seq, unsigned int field_idx)
912 {
913 	enum mail_cache_decision_type decision;
914 
915 	mail_cache_transaction_refresh_decisions(ctx);
916 
917 	decision = mail_cache_field_get_decision(ctx->view->cache, field_idx);
918 	if (decision == (MAIL_CACHE_DECISION_FORCED | MAIL_CACHE_DECISION_NO))
919 		return FALSE;
920 
921 	return mail_cache_field_exists(ctx->view, seq, field_idx) == 0;
922 }
923 
mail_cache_close_mail(struct mail_cache_transaction_ctx * ctx,uint32_t seq)924 void mail_cache_close_mail(struct mail_cache_transaction_ctx *ctx,
925 			   uint32_t seq)
926 {
927 	if (array_is_created(&ctx->cache_data_wanted_seqs))
928 		seq_range_array_remove(&ctx->cache_data_wanted_seqs, seq);
929 }
930