mail-transaction-log-view.c revision b5ac20e30146562322ceb7939f044d52d1e51184
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (C) 2003-2004 Timo Sirainen */
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen /* a list of log files we've referenced. we have to keep this list
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen explicitly because more files may be added into the linked list
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen at any time. */
213b139965e8bde6c8aff02ffd9fd39a74c887a9Timo Sirainen ARRAY_DEFINE(file_refs, struct mail_transaction_log_file *);
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen struct mail_transaction_log_file *cur, *head, *tail;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_transaction_log_view_open(struct mail_transaction_log *log)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen view = i_new(struct mail_transaction_log_view, 1);
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen array_append(&view->file_refs, &view->head, 1);
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainenmail_transaction_log_view_unref_all(struct mail_transaction_log_view *view)
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen struct mail_transaction_log_file *const *files;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen unsigned int i, count;
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen for (i = 0; i < count; i++)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainenvoid mail_transaction_log_view_close(struct mail_transaction_log_view **_view)
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen struct mail_transaction_log_view *view = *_view;
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen for (p = &view->log->views; *p != NULL; p = &(*p)->next) {
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainenvoid mail_transaction_log_views_close(struct mail_transaction_log *log)
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen for (view = log->views; view != NULL; view = view->next)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_transaction_log_view_set(struct mail_transaction_log_view *view,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uint32_t min_file_seq, uoff_t min_file_offset,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uint32_t max_file_seq, uoff_t max_file_offset,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_transaction_log_file *file, *first;
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen /* transaction log is closed already. this log view shouldn't
a050ca9def13949dbaa67bd6574a41c4f397ae26Timo Sirainen be used anymore. */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* index file doesn't exist yet. this transaction log should
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen start from the beginning */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen if (view->log->files->hdr.prev_file_seq != 0) {
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen /* but it doesn't */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen min_file_seq = view->log->files->hdr.file_seq;
45e62043058738e294f89504c319d852e25943ccTimo Sirainen if (min_file_seq == view->log->files->hdr.prev_file_seq &&
45e62043058738e294f89504c319d852e25943ccTimo Sirainen min_file_offset == view->log->files->hdr.prev_file_offset) {
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen /* we can skip this */
45e62043058738e294f89504c319d852e25943ccTimo Sirainen min_file_seq = view->log->files->hdr.file_seq;
9df8c9225140d9d1df5ddf4c6c9da61662ae6c44Timo Sirainen /* empty view */
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen /* find the oldest log file first. */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen ret = mail_transaction_log_find_file(view->log, min_file_seq, &file);
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen /* this could happen if internal transactions haven't yet been
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen committed but external are. just assume we're at the
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen beginning. */
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen if (max_file_offset == 0 && min_file_seq == max_file_seq)
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen i_assert(min_file_offset >= file->hdr.hdr_size);
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainen if (min_file_seq == max_file_seq && min_file_offset > max_file_offset) {
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainen /* log file offset is probably corrupted in the index file. */
95a284736b8b11319a3f575ba249ba2eb7dbac1bTimo Sirainen min_file_seq, min_file_offset, max_file_offset);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = mail_transaction_log_file_map(file, min_file_offset, end_offset);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen for (seq = min_file_seq+1; seq <= max_file_seq; seq++) {
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen if (file == NULL || file->hdr.file_seq != seq) {
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen /* see if we could find the missing file */
2a6af811ea3de3cf9e2f15e446674dd21b0705f3Timo Sirainen ret = mail_transaction_log_find_file(view->log,
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen /* not found / corrupted */
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen if (file == NULL || file->hdr.file_seq != seq) {
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen if (file == NULL && max_file_seq == (uint32_t)-1) {
e86d0d34fe365da4c7ca4312d575bfcbf3a01c0eTimo Sirainen /* we just wanted to sync everything */
fc7b17677ac1a5fa3f7fe13d5ef7dcfea8d9b4a1Timo Sirainen /* missing files in the middle */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen end_offset = file->hdr.file_seq == max_file_seq ?
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen ret = mail_transaction_log_file_map(file, file->hdr.hdr_size,
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen /* we have all of them. update refcounts. */
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen /* reference all used files */
bbce20cb4e5739e9a06058cf8ee1f38a7f6884f6Timo Sirainen for (file = view->tail; file != NULL; file = file->next) {
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen i_assert(view->cur_offset <= view->cur->sync_offset);
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen i_assert(view->cur->hdr.file_seq == min_file_seq);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_transaction_log_view_get_prev_pos(struct mail_transaction_log_view *view,
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainenmail_transaction_log_view_get_last(struct mail_transaction_log_view *view,
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen struct mail_transaction_log_file *cur = view->cur;
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if (cur->hdr.file_seq == view->max_file_seq) {
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* last file */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* we're all finished */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* end of file, go to next one */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* not EOF */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainenbool mail_transaction_log_view_is_last(struct mail_transaction_log_view *view)
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen return mail_transaction_log_view_get_last(view, &cur, &cur_offset);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_transaction_log_view_set_corrupted(struct mail_transaction_log_view *view,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *fmt, ...)
c1d45cada20777e1973579d40d0ebe43f89bb053Timo Sirainen mail_transaction_log_file_set_corrupted(view->log->head, "%s",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmail_transaction_log_view_is_corrupted(struct mail_transaction_log_view *view)
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainenlog_view_is_record_valid(struct mail_transaction_log_file *file,
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen const struct mail_transaction_type_map *type_rec)
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen rec_type = hdr->type & MAIL_TRANSACTION_TYPE_MASK;
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen rec_size = mail_index_offset_to_uint32(hdr->size) - sizeof(*hdr);
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* we want to be extra careful with expunges */
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen "expunge record missing protection mask");
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* records that are exported by syncing and view syncing will be
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen checked here so that we don't have to implement the same validation
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen multiple times. other records are checked internally by
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen mail_index_sync_record(). */
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen uid_buf = buffer_create_const_data(pool_datastack_create(),
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen uid_buf = buffer_create_const_data(pool_datastack_create(),
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen const struct mail_transaction_keyword_update *rec = data;
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen seqset_offset = sizeof(*rec) + rec->name_size;
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen ((rec_size - seqset_offset) % (sizeof(uint32_t)*2)) != 0) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen "Invalid keyword update record size");
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen uid_buf = buffer_create_const_data(pool_datastack_create(),
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen array_create_from_buffer(&uids, uid_buf, sizeof(uint32_t)*2);
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen uid_buf = buffer_create_const_data(pool_datastack_create(),
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen sizeof(struct mail_transaction_keyword_reset));
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if (rec->seq1 > rec->seq2 || rec->seq1 == 0) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen "Invalid UID range "
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen "(%u .. %u, type=0x%x)",
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if (prev != NULL && rec->seq1 <= prev->seq2) {
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen "Non-sorted UID ranges (type=0x%x)",
a8012fea2a7315033bc467acbf46be8e7323318cTimo Sirainenlog_view_get_next(struct mail_transaction_log_view *view,
834b90e1f426d1e3308670e09c050bcdea546eb8Timo Sirainen const void **data_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct mail_transaction_type_map *type_rec;
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen /* prev_file_offset should point to beginning of previous log record.
fadd878cd6098f5b873c21c121209a922679dae4Timo Sirainen when we reach EOF, it should be left there, not to beginning of the
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen next file that's not included inside the view. */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen if (mail_transaction_log_view_get_last(view, &view->cur,
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen /* if the last file was the beginning of a file, we want to
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen move prev pointers there */
c9c24293550541307f1bb41bba4a0fdfe2fa59e0Timo Sirainen view->prev_file_seq = view->cur->hdr.file_seq;
f2786c07cbd4a7a0a6a46c3e06dc4545aaf2f278Timo Sirainen view->prev_file_seq = view->cur->hdr.file_seq;
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen data = buffer_get_data(file->buffer, &file_size);
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen if (view->cur_offset + sizeof(*hdr) > file_size) {
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen "offset points outside file "
f1e1d821d93e4a1dc6ed8f23febde868b5d64cd5Timo Sirainen "(%"PRIuUOFF_T" + %"PRIuSIZE_T" > %"PRIuSIZE_T")",
6eb30032b4a50c383dea4c9c74342d906de6ad36Timo Sirainen i_assert(view->cur_offset >= file->buffer_offset);
a24f6b02ed8d0dde933a715be1c86f01977bf610Timo Sirainen hdr = CONST_PTR_OFFSET(data, view->cur_offset - file->buffer_offset);
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen rec_type = hdr->type & MAIL_TRANSACTION_TYPE_MASK;
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen full_size = mail_index_offset_to_uint32(hdr->size);
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen /* we'll fail below with "records size too small" */
5a07b37a9df398b5189c14872a600384208ab74bTimo Sirainen type_rec = mail_transaction_type_lookup(hdr->type);
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen "record size too small (type=0x%x, "
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen (full_size - sizeof(*hdr)) % record_size != 0) {
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen "record size wrong (type 0x%x, "
01ac83a86f9f29741b585205eefeec9c0c546f8bTimo Sirainen "offset=%"PRIuUOFF_T", size=%"PRIuSIZE_T" %% %u != 0)",
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen rec_type, view->cur_offset, (full_size - sizeof(*hdr)),
075912b4566a79c7bc59bf229c9f629ef7be0ea2Timo Sirainen if (file_size - view->cur_offset < full_size) {
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen "record size too large (type=0x%x, "
1225a5a7ce39f1d5545d5ed3b84ecd4f72438d36Timo Sirainen "offset=%"PRIuUOFF_T", size=%u, end=%"PRIuSIZE_T")",
b5ac20e30146562322ceb7939f044d52d1e51184Timo Sirainen rec_type, view->cur_offset, full_size, file_size);
c91de2744f8c1e61e91082ff5e214450f28a0e7cTimo Sirainen if (!log_view_is_record_valid(file, hdr, data, type_rec))
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint mail_transaction_log_view_next(struct mail_transaction_log_view *view,
834b90e1f426d1e3308670e09c050bcdea546eb8Timo Sirainen while ((ret = log_view_get_next(view, &hdr, &data)) > 0) {
6e354c4070b611471727692919d29440d73a73f7Timo Sirainen /* looks like this is within our mask, but expunge
6e354c4070b611471727692919d29440d73a73f7Timo Sirainen protection may mess up the check. */
6e354c4070b611471727692919d29440d73a73f7Timo Sirainen if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) == 0 ||
6e354c4070b611471727692919d29440d73a73f7Timo Sirainen (view->type_mask & MAIL_TRANSACTION_EXPUNGE) != 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we don't want this record */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* FIXME: hide flag/cache updates for appends if
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen append isn't in mask */
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainen mail_index_offset_to_uint32(view->tmp_hdr.size) - sizeof(*hdr);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* hide expunge protection */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen view->tmp_hdr.type &= ~MAIL_TRANSACTION_EXPUNGE_PROT;
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainenvoid mail_transaction_log_view_seek(struct mail_transaction_log_view *view,
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen i_assert(seq >= view->min_file_seq && seq <= view->max_file_seq);
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen i_assert(seq != view->min_file_seq || offset >= view->min_file_offset);
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen i_assert(seq != view->max_file_seq || offset < view->max_file_offset);
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen if (view->cur == NULL || seq != view->cur->hdr.file_seq) {
ccffbed92cb02c24fd717808a84138240bf1885bTimo Sirainen for (file = view->tail; file != NULL; file = file->next) {