mail-index-view-sync.c revision 6bc98d3898c475ba7615ba2b016e5142c8b2c09f
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "lib.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "array.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "buffer.h"
c2ee17c9c263efdc9c0a339c4836c3d43f5cd3d9Sascha Wilde#include "mail-index-view-private.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#include "mail-index-sync-private.h"
cda217260716cfd8d8ec5e56f91708c64c140538Timo Sirainen#include "mail-transaction-log.h"
7a7d2aa11e46195e2d92d6c337d7e78052a5ce67Timo Sirainen#include "mail-transaction-util.h"
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainenstruct mail_index_view_sync_ctx {
c2ee17c9c263efdc9c0a339c4836c3d43f5cd3d9Sascha Wilde struct mail_index_view *view;
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen enum mail_transaction_type visible_sync_mask;
c2ee17c9c263efdc9c0a339c4836c3d43f5cd3d9Sascha Wilde struct mail_index_sync_map_ctx sync_map_ctx;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen ARRAY_TYPE(seq_range) expunges;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen const struct mail_transaction_header *hdr;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen const void *data;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen size_t data_offset;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen unsigned int skipped_some:1;
f325d795b52ce2053f914072b22ebca9c4f0dc7eTimo Sirainen unsigned int last_read:1;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen unsigned int sync_map_update:1;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen};
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainenstatic int
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainenmail_transaction_log_sort_expunges(ARRAY_TYPE(seq_range) *expunges,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen const struct seq_range *src, size_t src_size)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* Note that all the sequences are actually still UIDs at this point */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen const struct seq_range *src_end;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen struct seq_range *dest, new_exp;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen unsigned int first, i, dest_count;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
139cbf8d9e1cc0c65d985f525756fe47a7bfada6Timo Sirainen i_assert(src_size % sizeof(*src) == 0);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen /* @UNSAFE */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen dest = array_get_modifiable(expunges, &dest_count);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (dest_count == 0) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen array_append(expunges, src, src_size / sizeof(*src));
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return 0;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen src_end = CONST_PTR_OFFSET(src, src_size);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen for (i = 0; src != src_end; src++) {
d29e35631075b8c172ce781b85fd1cdaf0a400fdTimo Sirainen /* src[] must be sorted. */
d29e35631075b8c172ce781b85fd1cdaf0a400fdTimo Sirainen if (src->seq1 > src->seq2 ||
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen (src+1 != src_end && src->seq2 >= src[1].seq1))
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return -1;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen for (; i < dest_count; i++) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (src->seq1 < dest[i].seq1)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen break;
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen new_exp = *src;
c8cf8a605e0ddea7cb36fe04551aeca5090e684bTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen first = i;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen while (i < dest_count && src->seq2 >= dest[i].seq1-1) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen /* we can/must merge with next record */
c8cf8a605e0ddea7cb36fe04551aeca5090e684bTimo Sirainen if (new_exp.seq2 < dest[i].seq2)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen new_exp.seq2 = dest[i].seq2;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen i++;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (first > 0 && new_exp.seq1 <= dest[first-1].seq2+1) {
139cbf8d9e1cc0c65d985f525756fe47a7bfada6Timo Sirainen /* continue previous record */
f6d63a21010540d3ddf08f2e7664ffca3ea70489Timo Sirainen if (dest[first-1].seq2 < new_exp.seq2)
139cbf8d9e1cc0c65d985f525756fe47a7bfada6Timo Sirainen dest[first-1].seq2 = new_exp.seq2;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen } else if (i == first) {
139cbf8d9e1cc0c65d985f525756fe47a7bfada6Timo Sirainen array_insert(expunges, i, &new_exp, 1);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen i++; first++;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen dest = array_get_modifiable(expunges, &dest_count);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen } else {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen /* use next record */
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen dest[first] = new_exp;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen first++;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (i > first) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen array_delete(expunges, first, i - first);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen dest = array_get_modifiable(expunges, &dest_count);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen i = first;
8d90e4f9a8f79f79c393aca23d0a897471dc2d8fTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return 0;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenstatic int view_sync_set_log_view_range(struct mail_index_view *view,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen enum mail_transaction_type type_mask)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen{
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen const struct mail_index_header *hdr = view->index->hdr;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen int ret;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen /* the view begins from the first non-synced transaction */
7fc0f80480063a9d4cb9e8c07b50db2a5627799eTimo Sirainen ret = mail_transaction_log_view_set(view->log_view,
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen view->log_file_seq,
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen view->log_file_offset,
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen hdr->log_file_seq,
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen hdr->log_file_index_int_offset,
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen type_mask);
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen if (ret <= 0) {
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen if (ret == 0) {
7fc0f80480063a9d4cb9e8c07b50db2a5627799eTimo Sirainen /* FIXME: use the new index to get needed changes */
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen mail_index_set_error(view->index,
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen "Transaction log got desynced for index %s",
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen view->index->filepath);
c2ee17c9c263efdc9c0a339c4836c3d43f5cd3d9Sascha Wilde mail_index_set_inconsistent(view->index);
c2ee17c9c263efdc9c0a339c4836c3d43f5cd3d9Sascha Wilde }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return -1;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return 0;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen}
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainenstatic int
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainenview_sync_get_expunges(struct mail_index_view *view,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen ARRAY_TYPE(seq_range) *expunges_r)
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen{
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen const struct mail_transaction_header *hdr;
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen struct seq_range *src, *src_end, *dest;
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen const void *data;
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen unsigned int count;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen int ret;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (view_sync_set_log_view_range(view, MAIL_TRANSACTION_EXPUNGE) < 0)
d29e35631075b8c172ce781b85fd1cdaf0a400fdTimo Sirainen return -1;
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen /* get a list of expunge transactions. there may be some that we have
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen already synced, but it doesn't matter because they'll get dropped
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen out when converting to sequences */
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen i_array_init(expunges_r, 64);
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen while ((ret = mail_transaction_log_view_next(view->log_view,
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen &hdr, &data, NULL)) > 0) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen i_assert((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (mail_transaction_log_sort_expunges(expunges_r, data,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen hdr->size) < 0) {
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen mail_transaction_log_view_set_corrupted(view->log_view,
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen "Corrupted expunge record");
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen ret = -1;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen break;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (ret < 0) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen array_free(expunges_r);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return -1;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen /* convert UIDs to sequences */
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen src = dest = array_get_modifiable(expunges_r, &count);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen src_end = src + count;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen for (; src != src_end; src++) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen ret = mail_index_lookup_uid_range(view, src->seq1,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen src->seq2,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen &dest->seq1,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen &dest->seq2);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen i_assert(ret == 0);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen
35083063d0e432d0cf78206b5929750e613ad772Timo Sirainen if (dest->seq1 == 0)
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen count--;
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen else
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen dest++;
35083063d0e432d0cf78206b5929750e613ad772Timo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen array_delete(expunges_r, count, array_count(expunges_r) - count);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return 0;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen}
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainenstatic void mail_index_view_hdr_update(struct mail_index_view *view,
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen struct mail_index_map *map)
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen{
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen /* Keep message count the same. */
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen map->hdr.next_uid = view->hdr.next_uid;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen map->hdr.messages_count = view->hdr.messages_count;
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen /* Keep the old message flag counts also, although they may be
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen somewhat stale already. We just don't want them to be more than
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen our old messages_count. */
b1c42176a65dbe9c83a0af766e6bd8315530f3a5Timo Sirainen map->hdr.recent_messages_count = view->hdr.recent_messages_count;
d927bc5618696157fc55eb1f11b5cab05400ed52Timo Sirainen map->hdr.seen_messages_count = view->hdr.seen_messages_count;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen map->hdr.deleted_messages_count = view->hdr.deleted_messages_count;
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen /* Keep log position so we know where to continue syncing */
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen map->hdr.log_file_seq = view->hdr.log_file_seq;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen map->hdr.log_file_index_int_offset =
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen view->hdr.log_file_index_int_offset;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen map->hdr.log_file_index_ext_offset =
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen view->hdr.log_file_index_ext_offset;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen view->hdr = map->hdr;
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen buffer_write(map->hdr_copy_buf, 0, &map->hdr, sizeof(map->hdr));
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen}
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen#ifdef DEBUG
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainenstatic void mail_index_view_check(struct mail_index_view *view)
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen{
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen unsigned int i, del = 0, recent = 0, seen = 0;
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen i_assert(view->hdr.deleted_messages_count ==
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen view->map->hdr.deleted_messages_count);
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen i_assert(view->hdr.recent_messages_count ==
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen view->map->hdr.recent_messages_count);
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen i_assert(view->hdr.seen_messages_count ==
0671e0ae0cfd8d5d671a0c2a75a070c8e2a39fecTimo Sirainen view->map->hdr.seen_messages_count);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
d927bc5618696157fc55eb1f11b5cab05400ed52Timo Sirainen for (i = 0; i < view->map->records_count; i++) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen const struct mail_index_record *rec;
d927bc5618696157fc55eb1f11b5cab05400ed52Timo Sirainen
d927bc5618696157fc55eb1f11b5cab05400ed52Timo Sirainen rec = MAIL_INDEX_MAP_IDX(view->map, i);
d927bc5618696157fc55eb1f11b5cab05400ed52Timo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (rec->flags & MAIL_DELETED) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen i_assert(rec->uid >= view->hdr.first_deleted_uid_lowwater);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen del++;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (rec->flags & MAIL_RECENT) {
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen i_assert(rec->uid >= view->hdr.first_recent_uid_lowwater);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen recent++;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen if (rec->flags & MAIL_SEEN)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen seen++;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen else
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen i_assert(rec->uid >= view->hdr.first_unseen_uid_lowwater);
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen }
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen i_assert(del == view->hdr.deleted_messages_count);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen i_assert(recent == view->hdr.recent_messages_count);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen i_assert(seen == view->hdr.seen_messages_count);
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen}
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#endif
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen#define MAIL_INDEX_VIEW_VISIBLE_FLAGS_MASK \
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen (MAIL_INDEX_SYNC_TYPE_FLAGS | \
2fb9ae42f9e36388ec6db24188b9108434043fd0Timo Sirainen MAIL_INDEX_SYNC_TYPE_KEYWORD_RESET | \
f77ffa31038d46ca9c6d24d93e3d76c9aa8d4d0cTimo Sirainen MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD | MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE)
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen#define MAIL_TRANSACTION_VISIBLE_SYNC_MASK \
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen (MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_APPEND | \
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen MAIL_TRANSACTION_FLAG_UPDATE | MAIL_TRANSACTION_KEYWORD_UPDATE | \
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen MAIL_TRANSACTION_KEYWORD_RESET)
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen#define VIEW_IS_SYNCED_TO_SAME(view, hdr) \
67c47dbb3fde79218320fd38a45c33f61bbf3012Timo Sirainen ((hdr)->log_file_seq == (view)->log_file_seq && \
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen (hdr)->log_file_index_int_offset == (view)->log_file_offset && \
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen (hdr)->log_file_index_ext_offset == (view)->log_file_offset && \
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen (!array_is_created(&view->syncs_done) || \
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen array_count(&view->syncs_done) == 0))
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenint mail_index_view_sync_begin(struct mail_index_view *view,
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen enum mail_index_view_sync_type sync_type,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct mail_index_view_sync_ctx **ctx_r)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen{
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen struct mail_index_view_sync_ctx *ctx;
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen struct mail_index_map *map;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen enum mail_transaction_type log_get_mask, visible_mask;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ARRAY_TYPE(seq_range) expunges = ARRAY_INIT;
1ba47b1a31e60c533631c8810400b365f785870aTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen i_assert(!view->syncing);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen i_assert(view->transactions == 0);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen if (mail_index_view_lock_head(view) < 0)
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen return -1;
9762e4f86950549c8186c7d3d4fa4a6b533ea848Timo Sirainen
eb64c3586d854cddd693f0b811d897399076a441Timo Sirainen if (sync_type == MAIL_INDEX_VIEW_SYNC_TYPE_ALL) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* get list of all expunges first */
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (view_sync_get_expunges(view, &expunges) < 0)
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen return -1;
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen }
c4c9be10781e1a16b3b001dc6b0461c4640da101Timo Sirainen
60b42c6dfdf9edcca8a96b380ef9a0adc60c2464Timo Sirainen /* only flags, appends and expunges can be left to be synced later */
7fc0f80480063a9d4cb9e8c07b50db2a5627799eTimo Sirainen switch (sync_type) {
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen case MAIL_INDEX_VIEW_SYNC_TYPE_ALL:
4ed1c0fedf33299264a319b2c4e0fe2465ea321bTimo Sirainen visible_mask = MAIL_TRANSACTION_VISIBLE_SYNC_MASK;
4ed1c0fedf33299264a319b2c4e0fe2465ea321bTimo Sirainen break;
4ed1c0fedf33299264a319b2c4e0fe2465ea321bTimo Sirainen case MAIL_INDEX_VIEW_SYNC_TYPE_NOAPPENDS_NOEXPUNGES:
4ed1c0fedf33299264a319b2c4e0fe2465ea321bTimo Sirainen visible_mask = MAIL_TRANSACTION_VISIBLE_SYNC_MASK &
60b42c6dfdf9edcca8a96b380ef9a0adc60c2464Timo Sirainen ~(MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_APPEND);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen break;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen case MAIL_INDEX_VIEW_SYNC_TYPE_NOEXPUNGES:
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen visible_mask = MAIL_TRANSACTION_VISIBLE_SYNC_MASK &
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen ~MAIL_TRANSACTION_EXPUNGE;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen break;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen }
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen /* we want to also get non-visible changes. especially because we use
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen the returned skipped-flag in mail_transaction_log_view_next() to
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen tell us if any visible changes were skipped. */
d29e35631075b8c172ce781b85fd1cdaf0a400fdTimo Sirainen log_get_mask = visible_mask | (MAIL_TRANSACTION_TYPE_MASK ^
d29e35631075b8c172ce781b85fd1cdaf0a400fdTimo Sirainen MAIL_TRANSACTION_VISIBLE_SYNC_MASK);
d29e35631075b8c172ce781b85fd1cdaf0a400fdTimo Sirainen if (view_sync_set_log_view_range(view, log_get_mask) < 0) {
d29e35631075b8c172ce781b85fd1cdaf0a400fdTimo Sirainen if (array_is_created(&expunges))
d29e35631075b8c172ce781b85fd1cdaf0a400fdTimo Sirainen array_free(&expunges);
d29e35631075b8c172ce781b85fd1cdaf0a400fdTimo Sirainen return -1;
d29e35631075b8c172ce781b85fd1cdaf0a400fdTimo Sirainen }
cc2c73be39dfe988f52c0370667e3882d01c63a2Timo Sirainen
cc2c73be39dfe988f52c0370667e3882d01c63a2Timo Sirainen ctx = i_new(struct mail_index_view_sync_ctx, 1);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ctx->view = view;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ctx->visible_sync_mask = visible_mask;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen ctx->expunges = expunges;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen mail_index_sync_map_init(&ctx->sync_map_ctx, view,
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen MAIL_INDEX_SYNC_HANDLER_VIEW);
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen if (sync_type == MAIL_INDEX_VIEW_SYNC_TYPE_ALL) {
c0a87e5f3316a57e6f915882fa1951d0fbb74a61Timo Sirainen view->sync_new_map = view->index->map;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen view->sync_new_map->refcount++;
94ce7e7700cda14a8342cb08e7285507b4b531daTimo Sirainen
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen /* since we're syncing everything, the counters get fixed */
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen view->broken_counters = FALSE;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen /* keep the old mapping without expunges until we're
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen fully synced */
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen } else {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen /* We need a private copy of the map if we don't want to
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen sync expunges.
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen If view's map is the head map, it means that it contains
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen already all the latest changes and there's no need for us
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen to apply any changes to it. This can only happen if there
ac26a4607cb12b156f6a42f1ead2881bedd43d94Timo Sirainen hadn't been any expunges. */
563273bdac80393af63b9520cbf4d24cc0efd028Timo Sirainen uint32_t old_records_count = view->map->records_count;
2615df45a8027948a474abe5e817b34b0499c171Timo Sirainen
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen if (view->map != view->index->map) {
77c462c3a415536f9c87028ee34546ee96fd1445Timo Sirainen /* Using non-head mapping. We have to apply
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainen transactions to it to get latest changes into it. */
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen ctx->sync_map_update = TRUE;
4654f788834c9d7920a351306b89cf5d1c21772eTimo Sirainen
/* Unless map was synced at the exact same position as
view, the message flags can't be reliably used to
update flag counters. note that map->hdr may contain
old information if another process updated the
index file since. */
if (view->map->mmap_base != NULL) {
const struct mail_index_header *hdr;
hdr = view->map->mmap_base;
view->map->hdr = *hdr;
}
ctx->sync_map_ctx.unreliable_flags =
!VIEW_IS_SYNCED_TO_SAME(view, &view->map->hdr);
/* Copy only the mails that we see currently, since
we're going to append the new ones when we see
their transactions. */
i_assert(view->map->records_count >=
view->hdr.messages_count);
view->map->records_count = view->hdr.messages_count;
#ifdef DEBUG
if (!ctx->sync_map_ctx.unreliable_flags) {
i_assert(view->map->hdr.messages_count ==
view->hdr.messages_count);
mail_index_view_check(view);
}
#endif
}
map = mail_index_map_clone(view->map);
view->map->records_count = old_records_count;
mail_index_unmap(view->index, &view->map);
view->map = map;
if (ctx->sync_map_update) {
/* Start the sync using our old view's header.
The old view->hdr may differ from map->hdr if
another view sharing the map with us had synced
itself. */
i_assert(map->hdr_base == map->hdr_copy_buf->data);
mail_index_view_hdr_update(view, map);
}
i_assert(map->records_count == map->hdr.messages_count);
}
/* Syncing the view invalidates all previous looked up records.
Unreference the mappings this view keeps because of them. */
mail_index_view_unref_maps(view);
view->syncing = TRUE;
*ctx_r = ctx;
return 0;
}
static void
view_add_synced_transaction(struct mail_index_view *view,
uint32_t log_file_seq, uoff_t log_file_offset)
{
struct mail_index_view_log_sync_area *pos;
if (!array_is_created(&view->syncs_done))
i_array_init(&view->syncs_done, 32);
pos = array_append_space(&view->syncs_done);
pos->log_file_seq = log_file_seq;
pos->log_file_offset = log_file_offset;
pos->length = 1;
}
static bool view_sync_area_find(ARRAY_TYPE(view_log_sync_area) *sync_arr,
uint32_t seq, uoff_t offset)
{
const struct mail_index_view_log_sync_area *syncs;
unsigned int i, count;
if (!array_is_created(sync_arr))
return FALSE;
syncs = array_get(sync_arr, &count);
for (i = 0; i < count; i++) {
if (syncs[i].log_file_offset <= offset &&
offset - syncs[i].log_file_offset < syncs[i].length &&
syncs[i].log_file_seq == seq)
return TRUE;
}
return FALSE;
}
static int
mail_index_view_sync_get_next_transaction(struct mail_index_view_sync_ctx *ctx)
{
struct mail_transaction_log_view *log_view = ctx->view->log_view;
struct mail_index_view *view = ctx->view;
uint32_t seq;
uoff_t offset;
int ret;
bool skipped, synced_to_map;
for (;;) {
/* Get the next transaction from log. */
ret = mail_transaction_log_view_next(log_view, &ctx->hdr,
&ctx->data, &skipped);
if (skipped) {
/* We skipped some (visible) transactions that were
outside our sync mask. Note that we may get here
even when ret=0. */
ctx->skipped_some = TRUE;
}
if (ret <= 0) {
if (ret < 0)
return -1;
ctx->hdr = NULL;
ctx->last_read = TRUE;
return 0;
}
mail_transaction_log_view_get_prev_pos(log_view, &seq, &offset);
if (!ctx->skipped_some) {
/* We haven't skipped anything while syncing this view.
Update this view's synced log offset. */
view->log_file_seq = seq;
view->log_file_offset = offset + sizeof(*ctx->hdr) +
ctx->hdr->size;
}
/* skip everything we've already synced */
if (view_sync_area_find(&view->syncs_done, seq, offset))
continue;
if (ctx->skipped_some) {
/* We've been skipping some transactions, which means
we'll go through these same transactions again
later. Since we're syncing this one, we don't want
to do it again. */
view_add_synced_transaction(view, seq, offset);
}
/* if we started from a map that we didn't create ourself,
some of the external transactions may already be synced.
at the end of view sync we'll update the ext_offset in the
header so that this check always becomes FALSE for
subsequent syncs. */
synced_to_map = offset < view->hdr.log_file_index_ext_offset &&
seq == view->hdr.log_file_seq &&
(ctx->hdr->type & MAIL_TRANSACTION_EXTERNAL) != 0;
/* Apply transaction to view's mapping if needed (meaning we
didn't just re-map the view to head mapping). */
if (ctx->sync_map_update && !synced_to_map) {
i_assert((ctx->hdr->type &
MAIL_TRANSACTION_EXPUNGE) == 0);
if (mail_index_sync_record(&ctx->sync_map_ctx,
ctx->hdr, ctx->data) < 0)
return -1;
}
if ((ctx->hdr->type & ctx->visible_sync_mask) == 0) {
/* non-visible change that we just wanted to update
to map. */
continue;
}
/* skip changes committed by hidden transactions (eg. in IMAP
store +flags.silent command) */
if (view_sync_area_find(&view->syncs_hidden, seq, offset))
continue;
break;
}
return 1;
}
#define FLAG_UPDATE_IS_INTERNAL(u) \
((((u)->add_flags | (u)->remove_flags) & \
~(MAIL_INDEX_MAIL_FLAG_DIRTY | MAIL_RECENT)) == 0)
static int
mail_index_view_sync_get_rec(struct mail_index_view_sync_ctx *ctx,
struct mail_index_view_sync_rec *rec)
{
const struct mail_transaction_header *hdr = ctx->hdr;
const void *data = ctx->data;
switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
case MAIL_TRANSACTION_APPEND: {
/* data contains the appended records, but we don't care */
rec->type = MAIL_INDEX_SYNC_TYPE_APPEND;
rec->uid1 = rec->uid2 = 0;
ctx->data_offset += hdr->size;
break;
}
case MAIL_TRANSACTION_EXPUNGE: {
const struct mail_transaction_expunge *exp =
CONST_PTR_OFFSET(data, ctx->data_offset);
/* data contains mail_transaction_expunge[] */
rec->type = MAIL_INDEX_SYNC_TYPE_EXPUNGE;
rec->uid1 = exp->uid1;
rec->uid2 = exp->uid2;
ctx->data_offset += sizeof(*exp);
break;
}
case MAIL_TRANSACTION_FLAG_UPDATE: {
const struct mail_transaction_flag_update *update =
CONST_PTR_OFFSET(data, ctx->data_offset);
/* data contains mail_transaction_flag_update[] */
for (;;) {
ctx->data_offset += sizeof(*update);
if (!FLAG_UPDATE_IS_INTERNAL(update))
break;
/* skip internal flag changes */
if (ctx->data_offset == ctx->hdr->size)
return 0;
update = CONST_PTR_OFFSET(data, ctx->data_offset);
}
rec->type = MAIL_INDEX_SYNC_TYPE_FLAGS;
rec->uid1 = update->uid1;
rec->uid2 = update->uid2;
break;
}
case MAIL_TRANSACTION_KEYWORD_UPDATE: {
const struct mail_transaction_keyword_update *update = data;
const uint32_t *uids;
/* data contains mail_transaction_keyword_update header,
the keyword name and an array of { uint32_t uid1, uid2; } */
if (ctx->data_offset == 0) {
/* skip over the header and name */
ctx->data_offset = sizeof(*update) + update->name_size;
if ((ctx->data_offset % 4) != 0)
ctx->data_offset += 4 - (ctx->data_offset % 4);
}
uids = CONST_PTR_OFFSET(data, ctx->data_offset);
rec->type = MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD;
rec->uid1 = uids[0];
rec->uid2 = uids[1];
ctx->data_offset += sizeof(uint32_t) * 2;
break;
}
case MAIL_TRANSACTION_KEYWORD_RESET: {
const struct mail_transaction_keyword_reset *reset =
CONST_PTR_OFFSET(data, ctx->data_offset);
/* data contains mail_transaction_keyword_reset[] */
rec->type = MAIL_INDEX_SYNC_TYPE_KEYWORD_RESET;
rec->uid1 = reset->uid1;
rec->uid2 = reset->uid2;
ctx->data_offset += sizeof(*reset);
break;
}
default:
i_unreached();
}
return 1;
}
int mail_index_view_sync_next(struct mail_index_view_sync_ctx *ctx,
struct mail_index_view_sync_rec *sync_rec)
{
int ret;
do {
if (ctx->hdr == NULL || ctx->data_offset == ctx->hdr->size) {
ret = mail_index_view_sync_get_next_transaction(ctx);
if (ret <= 0)
return ret;
ctx->data_offset = 0;
}
} while (!mail_index_view_sync_get_rec(ctx, sync_rec));
return 1;
}
void mail_index_view_sync_get_expunges(struct mail_index_view_sync_ctx *ctx,
const ARRAY_TYPE(seq_range) **expunges_r)
{
*expunges_r = &ctx->expunges;
}
static void
mail_index_view_sync_clean_log_syncs(struct mail_index_view_sync_ctx *ctx,
ARRAY_TYPE(view_log_sync_area) *sync_arr,
bool fast_clean)
{
struct mail_index_view *view = ctx->view;
const struct mail_index_view_log_sync_area *syncs;
uint32_t seq = view->log_file_seq;
uoff_t offset = view->log_file_offset;
unsigned int i, count;
if (!array_is_created(sync_arr))
return;
if (!ctx->skipped_some && fast_clean) {
/* Nothing skipped. Clean it up the quick way. */
array_clear(sync_arr);
return;
}
/* Clean up until view's current syncing position */
syncs = array_get(sync_arr, &count);
for (i = 0; i < count; i++) {
if ((syncs[i].log_file_offset + syncs[i].length > offset &&
syncs[i].log_file_seq == seq) ||
syncs[i].log_file_seq > seq)
break;
}
if (i > 0)
array_delete(sync_arr, 0, i);
}
void mail_index_view_sync_end(struct mail_index_view_sync_ctx **_ctx)
{
struct mail_index_view_sync_ctx *ctx = *_ctx;
struct mail_index_view *view = ctx->view;
i_assert(view->syncing);
*_ctx = NULL;
mail_index_sync_map_deinit(&ctx->sync_map_ctx);
mail_index_view_sync_clean_log_syncs(ctx, &view->syncs_done, TRUE);
mail_index_view_sync_clean_log_syncs(ctx, &view->syncs_hidden, FALSE);
if (!ctx->last_read) {
/* we didn't sync everything */
view->inconsistent = TRUE;
}
if (view->sync_new_map != NULL) {
mail_index_unmap(view->index, &view->map);
view->map = view->sync_new_map;
view->sync_new_map = NULL;
}
if (ctx->sync_map_update) {
if (view->log_file_seq != view->map->hdr.log_file_seq) {
i_assert(view->log_file_seq >
view->map->hdr.log_file_seq);
view->map->hdr.log_file_seq = view->log_file_seq;
view->map->hdr.log_file_index_ext_offset =
view->log_file_offset;
} else {
i_assert(view->log_file_offset >=
view->map->hdr.log_file_index_int_offset);
if (view->log_file_offset >
view->map->hdr.log_file_index_ext_offset) {
view->map->hdr.log_file_index_ext_offset =
view->log_file_offset;
}
}
view->map->hdr.log_file_index_int_offset =
view->log_file_offset;
buffer_write(view->map->hdr_copy_buf, 0,
&view->map->hdr, sizeof(view->map->hdr));
} else {
i_assert(view->inconsistent ||
view->log_file_offset >=
view->map->hdr.log_file_index_int_offset);
}
view->hdr = view->map->hdr;
#ifdef DEBUG
if (!view->broken_counters && !ctx->sync_map_ctx.unreliable_flags)
mail_index_view_check(view);
#endif
/* set log view to empty range so unneeded memory gets freed */
(void)mail_transaction_log_view_set(view->log_view,
view->log_file_seq,
view->log_file_offset,
view->log_file_seq,
view->log_file_offset,
MAIL_TRANSACTION_TYPE_MASK);
if (array_is_created(&ctx->expunges))
array_free(&ctx->expunges);
view->syncing = FALSE;
i_free(ctx);
}
void mail_index_view_add_hidden_transaction(struct mail_index_view *view,
uint32_t log_file_seq,
uoff_t log_file_offset,
unsigned int length)
{
struct mail_index_view_log_sync_area *area;
if (!array_is_created(&view->syncs_hidden))
i_array_init(&view->syncs_hidden, 32);
area = array_append_space(&view->syncs_hidden);
area->log_file_seq = log_file_seq;
area->log_file_offset = log_file_offset;
area->length = length;
}