mail-transaction-log-view.c revision a4d209d480d453566d331e870b8d0c99af7716c8
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2003-2008 Dovecot authors, see the included COPYING file */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "lib.h"
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen#include "array.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "mail-index-private.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "mail-transaction-log-private.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenstruct mail_transaction_log_view {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen struct mail_transaction_log *log;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen struct mail_transaction_log_view *next;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen uint32_t min_file_seq, max_file_seq;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen uoff_t min_file_offset, max_file_offset;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen struct mail_transaction_header tmp_hdr;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen /* a list of log files we've referenced. we have to keep this list
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen explicitly because more files may be added into the linked list
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen at any time. */
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen ARRAY_DEFINE(file_refs, struct mail_transaction_log_file *);
42dbeeb3462895b03e7633dbc59e8e191199734bTimo Sirainen struct mail_transaction_log_file *cur, *head, *tail;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen uoff_t cur_offset;
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen uint32_t prev_file_seq;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen uoff_t prev_file_offset;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen unsigned int broken:1;
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen};
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen
1a266561b099269bef75eee1a3742e61130ef780Timo Sirainenstruct mail_transaction_log_view *
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenmail_transaction_log_view_open(struct mail_transaction_log *log)
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen{
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen struct mail_transaction_log_view *view;
14175321ddb88619015866978c05a27786ca4814Timo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen view = i_new(struct mail_transaction_log_view, 1);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen view->log = log;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen view->broken = TRUE;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen view->head = view->tail = view->log->head;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen view->head->refcount++;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen i_array_init(&view->file_refs, 8);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen array_append(&view->file_refs, &view->head, 1);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen view->next = log->views;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen log->views = view;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen return view;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen}
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainenstatic void
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainenmail_transaction_log_view_unref_all(struct mail_transaction_log_view *view)
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen{
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen struct mail_transaction_log_file *const *files;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen unsigned int i, count;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen files = array_get(&view->file_refs, &count);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen for (i = 0; i < count; i++)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen files[i]->refcount--;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen array_clear(&view->file_refs);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen}
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainenvoid mail_transaction_log_view_close(struct mail_transaction_log_view **_view)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen{
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen struct mail_transaction_log_view *view = *_view;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen struct mail_transaction_log_view **p;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen *_view = NULL;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen for (p = &view->log->views; *p != NULL; p = &(*p)->next) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (*p == view) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen *p = view->next;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen break;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen mail_transaction_log_view_unref_all(view);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen mail_transaction_logs_clean(view->log);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen array_free(&view->file_refs);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen i_free(view);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen}
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainenvoid mail_transaction_log_views_close(struct mail_transaction_log *log)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen{
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen struct mail_transaction_log_view *view;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen for (view = log->views; view != NULL; view = view->next)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen view->log = NULL;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen}
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainenint mail_transaction_log_view_set(struct mail_transaction_log_view *view,
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen uint32_t min_file_seq, uoff_t min_file_offset,
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen uint32_t max_file_seq, uoff_t max_file_offset,
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen bool *reset_r)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen{
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen struct mail_transaction_log_file *file, *const *files;
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen uoff_t start_offset, end_offset;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen unsigned int i;
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen uint32_t seq;
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen int ret;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen i_assert(view->log != NULL);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen i_assert(min_file_seq <= max_file_seq);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen *reset_r = FALSE;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (view->log == NULL) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen /* transaction log is closed already. this log view shouldn't
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen be used anymore. */
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen return -1;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen }
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (min_file_seq == 0) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen /* index file doesn't exist yet. this transaction log should
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen start from the beginning */
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (view->log->files->hdr.prev_file_seq != 0) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen /* but it doesn't */
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen return 0;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen }
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen min_file_seq = view->log->files->hdr.file_seq;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen min_file_offset = 0;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (max_file_seq == 0) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen max_file_seq = min_file_seq;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen max_file_offset = min_file_offset;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (min_file_seq == view->log->files->hdr.prev_file_seq &&
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen min_file_offset == view->log->files->hdr.prev_file_offset) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen /* we can skip this */
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen min_file_seq = view->log->files->hdr.file_seq;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen min_file_offset = 0;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (min_file_seq > max_file_seq) {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen /* empty view */
3d2d1ba0a14521c7320370d8cafb9a95b49d333dTimo Sirainen max_file_seq = min_file_seq;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen max_file_offset = min_file_offset;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (min_file_seq == max_file_seq && min_file_offset > max_file_offset) {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen /* log file offset is probably corrupted in the index file. */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen mail_transaction_log_view_set_corrupted(view,
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen "file_seq=%u, min_file_offset (%"PRIuUOFF_T
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen ") > max_file_offset (%"PRIuUOFF_T")",
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen min_file_seq, min_file_offset, max_file_offset);
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen return -1;
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen }
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen if (min_file_offset > 0 && min_file_offset < view->tail->hdr.hdr_size) {
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen /* log file offset is probably corrupted in the index file. */
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen mail_transaction_log_view_set_corrupted(view,
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen "file_seq=%u, min_file_offset (%"PRIuUOFF_T
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen ") < hdr_size (%u)",
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen min_file_seq, min_file_offset,
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen view->tail->hdr.hdr_size);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen return -1;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen view->tail = view->head = file = NULL;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen for (seq = min_file_seq; seq <= max_file_seq; seq++) {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (file == NULL || file->hdr.file_seq != seq) {
5478609e31a7665ee108ded988a309673f221aa1Timo Sirainen /* see if we could find the missing file. if we know
553667c748977991590854426255e1c34a615f24Timo Sirainen the max. file sequence, make sure NFS attribute
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen cache gets flushed if necessary. */
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen bool nfs_flush = max_file_seq != (uint32_t)-1;
553667c748977991590854426255e1c34a615f24Timo Sirainen
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen ret = mail_transaction_log_find_file(view->log, seq,
5478609e31a7665ee108ded988a309673f221aa1Timo Sirainen nfs_flush, &file);
5478609e31a7665ee108ded988a309673f221aa1Timo Sirainen if (ret <= 0) {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (ret < 0)
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen return -1;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen /* not found / corrupted */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen file = NULL;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (file == NULL || file->hdr.file_seq != seq) {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (file == NULL && max_file_seq == (uint32_t)-1 &&
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen view->head == view->log->head) {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen /* we just wanted to sync everything */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen i_assert(max_file_offset == (uoff_t)-1);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen max_file_seq = seq-1;
597dba3488c648ffb375ee4a552bd52ac4346979Timo Sirainen break;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen /* if any of the found files reset the index,
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen ignore any missing files up to it */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen file = view->tail != NULL ? view->tail :
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen view->log->files;
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen for (;; file = file->next) {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (file == NULL ||
bf3fb941cc1deb06786449b89c77d9a56a07c251Timo Sirainen file->hdr.file_seq > max_file_seq) {
9511a40d933181045343110c8101b75887062aaeTimo Sirainen /* missing files in the middle */
9511a40d933181045343110c8101b75887062aaeTimo Sirainen return 0;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (file->hdr.file_seq >= seq &&
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen file->hdr.prev_file_seq == 0) {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen /* we can ignore the missing file */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen break;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen seq = file->hdr.file_seq;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen view->tail = NULL;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
if (view->tail == NULL)
view->tail = file;
view->head = file;
file = file->next;
}
if (min_file_offset == 0) {
/* beginning of the file */
min_file_offset = view->tail->hdr.hdr_size;
if (min_file_offset > max_file_offset &&
min_file_seq == max_file_seq) {
/* we don't actually want to show anything */
max_file_offset = min_file_offset;
}
}
i_assert(min_file_offset >= view->tail->hdr.hdr_size);
/* we have all of them. update refcounts. */
mail_transaction_log_view_unref_all(view);
/* Reference all used files. */
for (file = view->tail;; file = file->next) {
array_append(&view->file_refs, &file, 1);
file->refcount++;
if (file == view->head)
break;
}
view->cur = view->tail;
view->cur_offset = view->cur->hdr.file_seq == min_file_seq ?
min_file_offset : view->cur->hdr.hdr_size;
/* Map the files only after we've found them all. Otherwise if we map
one file and then another file just happens to get rotated, we could
include both files in the view but skip the last transactions from
the first file.
We're mapping the files in reverse order so that _log_file_map()
can verify that prev_file_offset matches how far it actually managed
to sync the file. */
files = array_idx(&view->file_refs, 0);
for (i = array_count(&view->file_refs); i > 0; i--) {
file = files[i-1];
start_offset = file->hdr.file_seq == min_file_seq ?
min_file_offset : file->hdr.hdr_size;
end_offset = file->hdr.file_seq == max_file_seq ?
max_file_offset : (uoff_t)-1;
ret = mail_transaction_log_file_map(file, start_offset,
end_offset);
if (ret <= 0)
return ret;
if (file->hdr.prev_file_seq == 0) {
/* this file resets the index.
don't bother reading the others. */
if (view->cur != file ||
view->cur_offset == file->hdr.hdr_size) {
view->cur = file;
view->cur_offset = file->hdr.hdr_size;
*reset_r = TRUE;
break;
}
i_assert(i == 1);
}
}
i_assert(max_file_seq == (uint32_t)-1 ||
max_file_seq == view->head->hdr.file_seq);
i_assert(max_file_offset == (uoff_t)-1 ||
max_file_offset <= view->head->sync_offset);
view->prev_file_seq = view->cur->hdr.file_seq;
view->prev_file_offset = view->cur_offset;
view->min_file_seq = min_file_seq;
view->min_file_offset = min_file_offset;
view->max_file_seq = max_file_seq;
view->max_file_offset = I_MIN(max_file_offset, view->head->sync_offset);
view->broken = FALSE;
i_assert(view->cur_offset <= view->cur->sync_offset);
return 1;
}
void mail_transaction_log_view_clear(struct mail_transaction_log_view *view,
uint32_t oldest_file_seq)
{
struct mail_transaction_log_file *file;
mail_transaction_log_view_unref_all(view);
if (mail_transaction_log_find_file(view->log, oldest_file_seq, FALSE,
&file) > 0) {
array_append(&view->file_refs, &file, 1);
file->refcount++;
}
view->cur = view->head = view->tail = NULL;
view->min_file_seq = view->max_file_seq = 0;
view->min_file_offset = view->max_file_offset = 0;
view->cur_offset = 0;
view->prev_file_seq = 0;
view->prev_file_offset = 0;
}
void
mail_transaction_log_view_get_prev_pos(struct mail_transaction_log_view *view,
uint32_t *file_seq_r,
uoff_t *file_offset_r)
{
*file_seq_r = view->prev_file_seq;
*file_offset_r = view->prev_file_offset;
}
static bool
mail_transaction_log_view_get_last(struct mail_transaction_log_view *view,
struct mail_transaction_log_file **last_r,
uoff_t *last_offset_r)
{
struct mail_transaction_log_file *cur = view->cur;
uoff_t cur_offset = view->cur_offset;
bool last = FALSE;
if (cur == NULL) {
*last_r = NULL;
return TRUE;
}
for (;;) {
if (cur->hdr.file_seq == view->max_file_seq) {
/* last file */
if (cur_offset == view->max_file_offset ||
cur_offset == cur->sync_offset) {
/* we're all finished */
last = TRUE;
}
} else if (cur_offset == cur->sync_offset) {
/* end of file, go to next one */
if (cur->next == NULL) {
last = TRUE;
} else {
cur = cur->next;
cur_offset = cur->hdr.hdr_size;
continue;
}
}
/* not EOF */
break;
}
*last_r = cur;
*last_offset_r = cur_offset;
return last;
}
bool mail_transaction_log_view_is_last(struct mail_transaction_log_view *view)
{
struct mail_transaction_log_file *cur;
uoff_t cur_offset;
return mail_transaction_log_view_get_last(view, &cur, &cur_offset);
}
void
mail_transaction_log_view_set_corrupted(struct mail_transaction_log_view *view,
const char *fmt, ...)
{
va_list va;
view->broken = TRUE;
va_start(va, fmt);
T_BEGIN {
mail_transaction_log_file_set_corrupted(view->log->head, "%s",
t_strdup_vprintf(fmt, va));
} T_END;
va_end(va);
}
bool
mail_transaction_log_view_is_corrupted(struct mail_transaction_log_view *view)
{
return view->broken;
}
static bool
log_view_is_record_valid(struct mail_transaction_log_file *file,
const struct mail_transaction_header *hdr,
const void *data)
{
enum mail_transaction_type rec_type;
ARRAY_TYPE(seq_range) uids = ARRAY_INIT;
buffer_t *uid_buf = NULL;
uint32_t rec_size;
bool ret = TRUE;
rec_type = hdr->type & MAIL_TRANSACTION_TYPE_MASK;
rec_size = mail_index_offset_to_uint32(hdr->size) - sizeof(*hdr);
/* we want to be extra careful with expunges */
if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0) {
if (rec_type != (MAIL_TRANSACTION_EXPUNGE |
MAIL_TRANSACTION_EXPUNGE_PROT)) {
mail_transaction_log_file_set_corrupted(file,
"expunge record missing protection mask");
return FALSE;
}
}
if (rec_size == 0) {
mail_transaction_log_file_set_corrupted(file,
"Empty record contents (type=0x%x)", rec_type);
return FALSE;
}
/* records that are exported by syncing and view syncing will be
checked here so that we don't have to implement the same validation
multiple times. other records are checked internally by
mail_index_sync_record(). */
switch (rec_type) {
case MAIL_TRANSACTION_APPEND:
if ((rec_size % sizeof(struct mail_index_record)) != 0) {
mail_transaction_log_file_set_corrupted(file,
"Invalid append record size");
ret = FALSE;
}
break;
case MAIL_TRANSACTION_EXPUNGE:
uid_buf = buffer_create_const_data(pool_datastack_create(),
data, rec_size);
array_create_from_buffer(&uids, uid_buf,
sizeof(struct mail_transaction_expunge));
break;
case MAIL_TRANSACTION_FLAG_UPDATE:
uid_buf = buffer_create_const_data(pool_datastack_create(),
data, rec_size);
array_create_from_buffer(&uids, uid_buf,
sizeof(struct mail_transaction_flag_update));
break;
case MAIL_TRANSACTION_KEYWORD_UPDATE: {
const struct mail_transaction_keyword_update *rec = data;
unsigned int seqset_offset;
seqset_offset = sizeof(*rec) + rec->name_size;
if ((seqset_offset % 4) != 0)
seqset_offset += 4 - (seqset_offset % 4);
if (seqset_offset > rec_size) {
mail_transaction_log_file_set_corrupted(file,
"Invalid keyword update record size");
ret = FALSE;
break;
}
uid_buf = buffer_create_const_data(pool_datastack_create(),
CONST_PTR_OFFSET(data, seqset_offset),
rec_size - seqset_offset);
array_create_from_buffer(&uids, uid_buf, sizeof(uint32_t)*2);
break;
}
case MAIL_TRANSACTION_KEYWORD_RESET:
uid_buf = buffer_create_const_data(pool_datastack_create(),
data, rec_size);
array_create_from_buffer(&uids, uid_buf,
sizeof(struct mail_transaction_keyword_reset));
break;
default:
break;
}
if (array_is_created(&uids)) {
const struct seq_range *rec, *prev = NULL;
unsigned int i, count = array_count(&uids);
if ((uid_buf->used % uids.arr.element_size) != 0) {
mail_transaction_log_file_set_corrupted(file,
"Invalid record size (type=0x%x)", rec_type);
ret = FALSE;
count = 0;
} else if (count == 0) {
mail_transaction_log_file_set_corrupted(file,
"No UID ranges (type=0x%x)", rec_type);
ret = FALSE;
}
for (i = 0; i < count; i++, prev = rec) {
rec = array_idx(&uids, i);
if (rec->seq1 > rec->seq2 || rec->seq1 == 0) {
mail_transaction_log_file_set_corrupted(file,
"Invalid UID range "
"(%u .. %u, type=0x%x)",
rec->seq1, rec->seq2, rec_type);
ret = FALSE;
break;
}
if (prev != NULL && rec->seq1 <= prev->seq2) {
mail_transaction_log_file_set_corrupted(file,
"Non-sorted UID ranges (type=0x%x)",
rec_type);
ret = FALSE;
break;
}
}
}
return ret;
}
static int
log_view_get_next(struct mail_transaction_log_view *view,
const struct mail_transaction_header **hdr_r,
const void **data_r)
{
const struct mail_transaction_header *hdr;
struct mail_transaction_log_file *file;
const void *data;
enum mail_transaction_type rec_type;
uint32_t full_size;
size_t file_size;
int ret;
if (view->cur == NULL)
return 0;
/* prev_file_offset should point to beginning of previous log record.
when we reach EOF, it should be left there, not to beginning of the
next file that's not included inside the view. */
if (mail_transaction_log_view_get_last(view, &view->cur,
&view->cur_offset)) {
/* if the last file was the beginning of a file, we want to
move prev pointers there */
view->prev_file_seq = view->cur->hdr.file_seq;
view->prev_file_offset = view->cur_offset;
view->cur = NULL;
return 0;
}
view->prev_file_seq = view->cur->hdr.file_seq;
view->prev_file_offset = view->cur_offset;
file = view->cur;
data = buffer_get_data(file->buffer, &file_size);
file_size += file->buffer_offset;
if (view->cur_offset + sizeof(*hdr) > file_size) {
mail_transaction_log_file_set_corrupted(file,
"offset points outside file "
"(%"PRIuUOFF_T" + %"PRIuSIZE_T" > %"PRIuSIZE_T")",
view->cur_offset, sizeof(*hdr), file_size);
return -1;
}
i_assert(view->cur_offset >= file->buffer_offset);
hdr = CONST_PTR_OFFSET(data, view->cur_offset - file->buffer_offset);
data = CONST_PTR_OFFSET(hdr, sizeof(*hdr));
rec_type = hdr->type & MAIL_TRANSACTION_TYPE_MASK;
full_size = mail_index_offset_to_uint32(hdr->size);
if (full_size < sizeof(*hdr)) {
mail_transaction_log_file_set_corrupted(file,
"record size too small (type=0x%x, "
"offset=%"PRIuUOFF_T", size=%u)",
rec_type, view->cur_offset, full_size);
return -1;
}
if (file_size - view->cur_offset < full_size) {
mail_transaction_log_file_set_corrupted(file,
"record size too large (type=0x%x, "
"offset=%"PRIuUOFF_T", size=%u, end=%"PRIuSIZE_T")",
rec_type, view->cur_offset, full_size, file_size);
return -1;
}
T_BEGIN {
ret = log_view_is_record_valid(file, hdr, data) ? 1 : -1;
} T_END;
if (ret > 0) {
*hdr_r = hdr;
*data_r = data;
view->cur_offset += full_size;
}
return ret;
}
int mail_transaction_log_view_next(struct mail_transaction_log_view *view,
const struct mail_transaction_header **hdr_r,
const void **data_r)
{
const struct mail_transaction_header *hdr;
const void *data;
int ret = 0;
if (view->broken)
return -1;
ret = log_view_get_next(view, &hdr, &data);
if (ret <= 0) {
if (ret < 0)
view->cur_offset = view->cur->sync_offset;
return ret;
}
/* drop expunge protection */
if ((hdr->type & MAIL_TRANSACTION_TYPE_MASK) ==
(MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT))
view->tmp_hdr.type = hdr->type & ~MAIL_TRANSACTION_EXPUNGE_PROT;
else
view->tmp_hdr.type = hdr->type;
/* return record's size */
view->tmp_hdr.size = mail_index_offset_to_uint32(hdr->size);
i_assert(view->tmp_hdr.size > sizeof(*hdr));
view->tmp_hdr.size -= sizeof(*hdr);
*hdr_r = &view->tmp_hdr;
*data_r = data;
return 1;
}
void mail_transaction_log_view_seek(struct mail_transaction_log_view *view,
uint32_t seq, uoff_t offset)
{
struct mail_transaction_log_file *file;
i_assert(seq >= view->min_file_seq && seq <= view->max_file_seq);
i_assert(seq != view->min_file_seq || offset >= view->min_file_offset);
i_assert(seq != view->max_file_seq || offset <= view->max_file_offset);
if (view->cur == NULL || seq != view->cur->hdr.file_seq) {
for (file = view->tail; file != NULL; file = file->next) {
if (file->hdr.file_seq == seq)
break;
}
i_assert(file != NULL);
view->cur = file;
}
view->cur_offset = offset;
}