Mercurial > dovecot > original-hg > dovecot-1.2
view src/lib-index/mail-transaction-log-view.c @ 4842:57c5e40e26dd HEAD
Compile fix
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 19 Nov 2006 16:35:55 +0200 |
parents | 7cc1b8fec8ab |
children | 987397c331d7 |
line wrap: on
line source
/* Copyright (C) 2003-2004 Timo Sirainen */ #include "lib.h" #include "array.h" #include "mail-index-private.h" #include "mail-transaction-log-private.h" #include "mail-transaction-util.h" struct mail_transaction_log_view { struct mail_transaction_log *log; struct mail_transaction_log_view *next; uint32_t min_file_seq, max_file_seq; uoff_t min_file_offset, max_file_offset; enum mail_transaction_type type_mask; struct mail_transaction_header tmp_hdr; /* a list of log files we've referenced. we have to keep this list explicitly because more files may be added into the linked list at any time. */ ARRAY_DEFINE(file_refs, struct mail_transaction_log_file *); struct mail_transaction_log_file *cur, *head, *tail; uoff_t cur_offset; uint32_t prev_file_seq; uoff_t prev_file_offset; unsigned int broken:1; }; struct mail_transaction_log_view * mail_transaction_log_view_open(struct mail_transaction_log *log) { struct mail_transaction_log_view *view; view = i_new(struct mail_transaction_log_view, 1); view->log = log; view->broken = TRUE; view->head = view->tail = view->log->head; view->head->refcount++; i_array_init(&view->file_refs, 8); array_append(&view->file_refs, &view->head, 1); view->next = log->views; log->views = view; return view; } static void mail_transaction_log_view_unref_all(struct mail_transaction_log_view *view) { struct mail_transaction_log_file *const *files; unsigned int i, count; files = array_get(&view->file_refs, &count); for (i = 0; i < count; i++) files[i]->refcount--; array_clear(&view->file_refs); } void mail_transaction_log_view_close(struct mail_transaction_log_view **_view) { struct mail_transaction_log_view *view = *_view; struct mail_transaction_log_view **p; *_view = NULL; for (p = &view->log->views; *p != NULL; p = &(*p)->next) { if (*p == view) { *p = view->next; break; } } mail_transaction_log_view_unref_all(view); mail_transaction_logs_clean(view->log); array_free(&view->file_refs); i_free(view); } void mail_transaction_log_views_close(struct mail_transaction_log *log) { struct mail_transaction_log_view *view; for (view = log->views; view != NULL; view = view->next) view->log = NULL; } int mail_transaction_log_view_set(struct mail_transaction_log_view *view, uint32_t min_file_seq, uoff_t min_file_offset, uint32_t max_file_seq, uoff_t max_file_offset, enum mail_transaction_type type_mask) { struct mail_transaction_log_file *file, *first; uint32_t seq; uoff_t end_offset; int ret; i_assert(view->log != NULL); i_assert(min_file_seq <= max_file_seq); if (view->log == NULL) { /* transaction log is closed already. this log view shouldn't be used anymore. */ return -1; } if (min_file_seq == 0) { /* new index, transaction file not synced yet */ min_file_seq = 1; min_file_offset = 0; if (max_file_seq == 0) { max_file_seq = min_file_seq; max_file_offset = min_file_offset; } } if (min_file_seq == view->log->files->hdr.prev_file_seq && min_file_offset == view->log->files->hdr.prev_file_offset) { /* we can skip this */ min_file_seq = view->log->files->hdr.file_seq; min_file_offset = 0; if (min_file_seq > max_file_seq) { /* empty view */ max_file_seq = min_file_seq; max_file_offset = min_file_offset; } } /* find the oldest log file first. */ ret = mail_transaction_log_file_find(view->log, min_file_seq, &file); if (ret <= 0) return ret; if (min_file_offset == 0) { /* this could happen if internal transactions haven't yet been committed but external are. just assume we're at the beginning. */ min_file_offset = file->hdr.hdr_size; if (max_file_offset == 0 && min_file_seq == max_file_seq) max_file_offset = min_file_offset; } /* check these later than others as index file may have corrupted log_file_offset. we should have recreated the log file and skipped min_file_seq file above.. max_file_offset can be broken only if min_file_seq = max_file_seq. */ i_assert(min_file_offset >= file->hdr.hdr_size); i_assert(min_file_seq != max_file_seq || min_file_offset <= max_file_offset); end_offset = min_file_seq == max_file_seq ? max_file_offset : (uoff_t)-1; ret = mail_transaction_log_file_map(file, min_file_offset, end_offset); if (ret <= 0) return ret; first = file; for (seq = min_file_seq+1; seq <= max_file_seq; seq++) { file = file->next; if (file == NULL || file->hdr.file_seq != seq) { /* see if we could find the missing file */ ret = mail_transaction_log_file_find(view->log, seq, &file); if (ret <= 0) { if (ret < 0) return -1; /* not found / corrupted */ file = NULL; } } if (file == NULL || file->hdr.file_seq != seq) { if (file == NULL && max_file_seq == (uint32_t)-1) { /* we just wanted to sync everything */ max_file_seq = seq-1; break; } /* missing files in the middle */ return 0; } end_offset = file->hdr.file_seq == max_file_seq ? max_file_offset : (uoff_t)-1; ret = mail_transaction_log_file_map(file, file->hdr.hdr_size, end_offset); if (ret <= 0) return ret; } i_assert(max_file_offset == (uoff_t)-1 || max_file_offset <= file->sync_offset); /* we have all of them. update refcounts. */ mail_transaction_log_view_unref_all(view); view->tail = first; view->head = view->log->head; /* reference all used files */ for (file = view->tail; file != NULL; file = file->next) { array_append(&view->file_refs, &file, 1); file->refcount++; } view->prev_file_seq = 0; view->prev_file_offset = 0; view->cur = first; view->cur_offset = min_file_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 = max_file_offset; view->type_mask = type_mask; view->broken = FALSE; i_assert(view->cur_offset <= view->cur->sync_offset); i_assert(view->cur->hdr.file_seq == min_file_seq); return 1; } 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; } 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_push(); mail_transaction_log_file_set_corrupted(view->log->head, "%s", t_strdup_vprintf(fmt, va)); t_pop(); va_end(va); } bool mail_transaction_log_view_is_corrupted(struct mail_transaction_log_view *view) { return view->broken; } 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 struct mail_transaction_type_map *type_rec; const void *data; unsigned int record_size; uint32_t hdr_size; size_t file_size; 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. */ view->prev_file_seq = view->cur->hdr.file_seq; view->prev_file_offset = view->cur_offset; if (view->cur->hdr.file_seq == view->max_file_seq) { /* last file */ if (view->cur_offset == view->max_file_offset || view->cur_offset == view->cur->sync_offset) { /* we're all finished */ view->cur = NULL; return 0; } } else if (view->cur_offset == view->cur->sync_offset) { /* end of file, go to next one */ view->cur = view->cur->next; if (view->cur == NULL) return 0; view->cur_offset = view->cur->hdr.hdr_size; return log_view_get_next(view, hdr_r, data_r); } 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)); hdr_size = mail_index_offset_to_uint32(hdr->size); if (hdr_size < sizeof(*hdr)) { type_rec = NULL; record_size = 0; } else { type_rec = mail_transaction_type_lookup(hdr->type); if (type_rec != NULL) record_size = type_rec->record_size; else { mail_transaction_log_file_set_corrupted(file, "unknown record type 0x%x", hdr->type & MAIL_TRANSACTION_TYPE_MASK); return -1; } } if (hdr_size < sizeof(*hdr) + record_size) { mail_transaction_log_file_set_corrupted(file, "record size too small (type=0x%x, " "offset=%"PRIuUOFF_T", size=%u)", hdr->type & MAIL_TRANSACTION_TYPE_MASK, view->cur_offset, hdr_size); return -1; } if ((hdr_size - sizeof(*hdr)) % record_size != 0) { mail_transaction_log_file_set_corrupted(file, "record size wrong (type 0x%x, " "offset=%"PRIuUOFF_T", size=%"PRIuSIZE_T" %% %u != 0)", hdr->type & MAIL_TRANSACTION_TYPE_MASK, view->cur_offset, (hdr_size - sizeof(*hdr)), record_size); return -1; } if (file_size - view->cur_offset < hdr_size) { mail_transaction_log_file_set_corrupted(file, "record size too large (type=0x%x, " "offset=%"PRIuUOFF_T", size=%u, end=%"PRIuSIZE_T")", hdr->type & MAIL_TRANSACTION_TYPE_MASK, view->cur_offset, hdr_size, file_size); return -1; } if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0) { if ((hdr->type & MAIL_TRANSACTION_TYPE_MASK) != (MAIL_TRANSACTION_EXPUNGE|MAIL_TRANSACTION_EXPUNGE_PROT)) { mail_transaction_log_file_set_corrupted(file, "found expunge without protection mask"); return -1; } } else if ((hdr->type & MAIL_TRANSACTION_TYPE_MASK) != type_rec->type) { mail_transaction_log_file_set_corrupted(file, "extra bits in header type: 0x%x", hdr->type & MAIL_TRANSACTION_TYPE_MASK); return -1; } else if (hdr->type == MAIL_TRANSACTION_EXT_INTRO) { const struct mail_transaction_ext_intro *intro; uint32_t i; for (i = 0; i < hdr_size; ) { if (i + sizeof(*intro) > hdr_size) { /* should be just extra padding */ break; } intro = CONST_PTR_OFFSET(data, i); if (intro->name_size > hdr_size - sizeof(*hdr) - sizeof(*intro)) { mail_transaction_log_file_set_corrupted(file, "extension intro: name_size too large"); return -1; } i += sizeof(*intro) + intro->name_size; } } *hdr_r = hdr; *data_r = data; view->cur_offset += hdr_size; return 1; } int mail_transaction_log_view_next(struct mail_transaction_log_view *view, const struct mail_transaction_header **hdr_r, const void **data_r, bool *skipped_r) { const struct mail_transaction_header *hdr; const void *data; int ret = 0; if (skipped_r != NULL) *skipped_r = FALSE; if (view->broken) return -1; while ((ret = log_view_get_next(view, &hdr, &data)) > 0) { if ((view->type_mask & hdr->type) != 0) { /* looks like this is within our mask, but expunge protection may mess up the check. */ if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) == 0 || (view->type_mask & MAIL_TRANSACTION_EXPUNGE) != 0) break; } /* we don't want this record */ if (skipped_r != NULL) *skipped_r = TRUE; /* FIXME: hide flag/cache updates for appends if append isn't in mask */ } if (ret < 0) { view->cur_offset = view->cur->sync_offset; return -1; } if (ret == 0) return 0; view->tmp_hdr = *hdr; view->tmp_hdr.size = mail_index_offset_to_uint32(view->tmp_hdr.size) - sizeof(*hdr); i_assert(view->tmp_hdr.size != 0); if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0) { /* hide expunge protection */ view->tmp_hdr.type &= ~MAIL_TRANSACTION_EXPUNGE_PROT; } *hdr_r = &view->tmp_hdr; *data_r = data; return 1; }