mail-transaction-log-view.c revision b5b3b4c9159f506cdfdce7399faaeeffdf73faf7
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen/* Copyright (c) 2003-2010 Dovecot authors, see the included COPYING file */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmail_transaction_log_view_open(struct mail_transaction_log *log)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen view = i_new(struct mail_transaction_log_view, 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen array_append(&view->file_refs, &view->head, 1);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmail_transaction_log_view_unref_all(struct mail_transaction_log_view *view)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_transaction_log_file *const *files;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int i, count;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen for (i = 0; i < count; i++)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenvoid mail_transaction_log_view_close(struct mail_transaction_log_view **_view)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_transaction_log_view *view = *_view;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen for (p = &view->log->views; *p != NULL; p = &(*p)->next) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainenvoid mail_transaction_log_views_close(struct mail_transaction_log *log)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen for (view = log->views; view != NULL; view = view->next)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenint mail_transaction_log_view_set(struct mail_transaction_log_view *view,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen uint32_t min_file_seq, uoff_t min_file_offset,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen uint32_t max_file_seq, uoff_t max_file_offset,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_transaction_log_file *file, *const *files, *tail;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen unsigned int i;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* transaction log is closed already. this log view shouldn't
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen be used anymore. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* index file doesn't exist yet. this transaction log should
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen start from the beginning */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* but it doesn't */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (min_file_seq == tail->hdr.prev_file_seq &&
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen min_file_offset == tail->hdr.prev_file_offset) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* we can skip this */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* empty view */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (min_file_seq == max_file_seq && min_file_offset > max_file_offset) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen /* log file offset is probably corrupted in the index file. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen min_file_seq, min_file_offset, max_file_offset);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (min_file_offset > 0 && min_file_offset < tail->hdr.hdr_size) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* log file offset is probably corrupted in the index file. */
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen ") < hdr_size (%u)",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen min_file_seq, min_file_offset, tail->hdr.hdr_size);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen for (seq = min_file_seq; seq <= max_file_seq; seq++) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (file == NULL || file->hdr.file_seq != seq) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* see if we could find the missing file. if we know
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen the max. file sequence, make sure NFS attribute
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen cache gets flushed if necessary. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen bool nfs_flush = max_file_seq != (uint32_t)-1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ret = mail_transaction_log_find_file(view->log, seq,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* not found / corrupted */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (file == NULL || file->hdr.file_seq != seq) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (file == NULL && max_file_seq == (uint32_t)-1 &&
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* we just wanted to sync everything */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* if any of the found files reset the index,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ignore any missing files up to it */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* missing files in the middle */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* we can ignore the missing file */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* beginning of the file */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* we don't actually want to show anything */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(min_file_offset >= view->tail->hdr.hdr_size);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (min_file_seq == view->head->hdr.file_seq &&
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* log file offset is probably corrupted in the index file. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ") > sync_offset (%"PRIuUOFF_T")", min_file_seq,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* we have all of them. update refcounts. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* Reference all used files. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen view->cur_offset = view->cur->hdr.file_seq == min_file_seq ?
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* Map the files only after we've found them all. Otherwise if we map
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen one file and then another file just happens to get rotated, we could
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen include both files in the view but skip the last transactions from
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen the first file.
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen We're mapping the files in reverse order so that _log_file_map()
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen can verify that prev_file_offset matches how far it actually managed
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen to sync the file. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen for (i = array_count(&view->file_refs); i > 0; i--) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen start_offset = file->hdr.file_seq == min_file_seq ?
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen end_offset = file->hdr.file_seq == max_file_seq ?
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ret = mail_transaction_log_file_map(file, start_offset,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* this file resets the index.
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen don't bother reading the others. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen view->prev_file_seq = view->cur->hdr.file_seq;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen view->max_file_offset = I_MIN(max_file_offset, view->head->sync_offset);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (mail_transaction_log_file_get_highest_modseq_at(view->cur,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(view->cur_offset <= view->cur->sync_offset);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenvoid mail_transaction_log_view_clear(struct mail_transaction_log_view *view,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (mail_transaction_log_find_file(view->log, oldest_file_seq, FALSE,
c8c4bbf6b1415e9d0845bc8f1cd6d19b76ab0392Timo Sirainen view->min_file_offset = view->max_file_offset = 0;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmail_transaction_log_view_get_prev_pos(struct mail_transaction_log_view *view,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmail_transaction_log_view_get_prev_modseq(struct mail_transaction_log_view *view)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmail_transaction_log_view_get_last(struct mail_transaction_log_view *view,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen struct mail_transaction_log_file *cur = view->cur;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (cur->hdr.file_seq == view->max_file_seq) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* last file */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* we're all finished */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* end of file, go to next one */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* not EOF */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenbool mail_transaction_log_view_is_last(struct mail_transaction_log_view *view)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen return mail_transaction_log_view_get_last(view, &cur, &cur_offset);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainenmail_transaction_log_view_set_corrupted(struct mail_transaction_log_view *view,
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen const char *fmt, ...)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_transaction_log_file_set_corrupted(view->log->head, "%s",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenmail_transaction_log_view_is_corrupted(struct mail_transaction_log_view *view)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenlog_view_is_uid_range_valid(struct mail_transaction_log_file *file,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if ((uids->arr.buffer->used % uids->arr.element_size) != 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen } else if (count == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (rec->seq1 > rec->seq2 || rec->seq1 == 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "Invalid UID range (%u .. %u, type=0x%x)",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (prev != NULL && rec->seq1 <= prev->seq2) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "Non-sorted UID ranges (type=0x%x)", rec_type);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainenlog_view_is_record_valid(struct mail_transaction_log_file *file,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen rec_type = hdr->type & MAIL_TRANSACTION_TYPE_MASK;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen rec_size = mail_index_offset_to_uint32(hdr->size) - sizeof(*hdr);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* we want to be extra careful with expunges */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "expunge record missing protection mask");
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if ((hdr->type & MAIL_TRANSACTION_EXPUNGE_GUID) != 0) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (rec_type != (MAIL_TRANSACTION_EXPUNGE_GUID |
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "expunge guid record missing protection mask");
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainen "Empty record contents (type=0x%x)", rec_type);
0e7d5ff38f28d8c85e197a031bbb66b322ff89e6Timo Sirainen /* records that are exported by syncing and view syncing will be
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen checked here so that we don't have to implement the same validation
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen multiple times. other records are checked internally by
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_index_sync_record(). */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if ((rec_size % sizeof(struct mail_index_record)) != 0) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "Invalid append record size");
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen buffer_create_const_data(&uid_buf, data, rec_size);
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if ((rec_size % sizeof(struct mail_transaction_expunge_guid)) != 0) {
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen "Invalid expunge guid record size");
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen buffer_create_const_data(&uid_buf, data, rec_size);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const struct mail_transaction_keyword_update *rec = data;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen seqset_offset = sizeof(*rec) + rec->name_size;
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen "Invalid keyword update record size");
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen buffer_create_const_data(&uid_buf, data, rec_size);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen sizeof(struct mail_transaction_keyword_reset));
b10c3f9ed997748fdbb03b9daadc8c31eed02120Timo Sirainen if (!log_view_is_uid_range_valid(file, rec_type, &uids))
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenlog_view_get_next(struct mail_transaction_log_view *view,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const void **data_r)
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* prev_file_offset should point to beginning of previous log record.
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen when we reach EOF, it should be left there, not to beginning of the
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen next file that's not included inside the view. */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (mail_transaction_log_view_get_last(view, &view->cur,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen /* if the last file was the beginning of a file, we want to
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen move prev pointers there */
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen view->prev_file_seq = view->cur->hdr.file_seq;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen view->prev_file_seq = view->cur->hdr.file_seq;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen data = buffer_get_data(file->buffer, &file_size);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (view->cur_offset + sizeof(*hdr) > file_size) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "offset points outside file "
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "(%"PRIuUOFF_T" + %"PRIuSIZE_T" > %"PRIuSIZE_T")",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen i_assert(view->cur_offset >= file->buffer_offset);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen hdr = CONST_PTR_OFFSET(data, view->cur_offset - file->buffer_offset);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen rec_type = hdr->type & MAIL_TRANSACTION_TYPE_MASK;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen full_size = mail_index_offset_to_uint32(hdr->size);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "record size too small (type=0x%x, "
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen if (file_size - view->cur_offset < full_size) {
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "record size too large (type=0x%x, "
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen "offset=%"PRIuUOFF_T", size=%u, end=%"PRIuSIZE_T")",
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen rec_type, view->cur_offset, full_size, file_size);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen ret = log_view_is_record_valid(file, hdr, data) ? 1 : -1;
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen mail_transaction_update_modseq(hdr, data, &view->prev_modseq);
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainenint mail_transaction_log_view_next(struct mail_transaction_log_view *view,
c8593b070319d0ff83f8d6c4b5ed5abf2d578a06Timo Sirainen const void **data_r)
if (ret <= 0) {
if (ret < 0)
return ret;