static int mail_cache_lookup_iter_next_record(struct mail_cache_lookup_iterate_ctx *ctx) { struct mail_cache_view *view = ctx->view; if (ctx->failed) return -1; if (ctx->rec != NULL) ctx->offset = ctx->rec->prev_offset; if (ctx->offset == 0) { /* end of this record list. check newly appended data. */ if (view->trans_seq1 > ctx->seq || view->trans_seq2 < ctx->seq) return 0; /* check data still in memory. this works for recent mails even with INDEX=MEMORY */ if (!ctx->memory_appends_checked) { if (mail_cache_lookup_iter_transaction(ctx)) return 1; ctx->memory_appends_checked = TRUE; } if (MAIL_CACHE_IS_UNUSABLE(view->cache)) return 0; /* check data already written to cache file */ if (ctx->disk_appends_checked || mail_cache_lookup_offset(view->cache, view->trans_view, ctx->seq, &ctx->offset) <= 0) return 0; ctx->disk_appends_checked = TRUE; ctx->remap_counter = view->cache->remap_counter; memset(&view->loop_track, 0, sizeof(view->loop_track)); } if (ctx->stop) return 0; /* look up the next record */ if (mail_cache_get_record(view->cache, ctx->offset, &ctx->rec) < 0) return -1; if (mail_cache_track_loops(&view->loop_track, ctx->offset, ctx->rec->size)) { mail_cache_set_corrupted(view->cache, "record list is circular"); return -1; } ctx->remap_counter = view->cache->remap_counter; ctx->pos = sizeof(*ctx->rec); ctx->rec_size = ctx->rec->size; return 1; }
int mail_cache_lookup_iter_next(struct mail_cache_lookup_iterate_ctx *ctx, struct mail_cache_iterate_field *field_r) { struct mail_cache *cache = ctx->view->cache; unsigned int field_idx; unsigned int data_size; uint32_t file_field; int ret; i_assert(ctx->remap_counter == cache->remap_counter); if (ctx->pos + sizeof(uint32_t) > ctx->rec_size) { if (ctx->pos != ctx->rec_size) { mail_cache_set_corrupted(cache, "record has invalid size"); return -1; } if ((ret = mail_cache_lookup_iter_next_record(ctx)) <= 0) return ret; } /* return the next field */ file_field = *((const uint32_t *)CONST_PTR_OFFSET(ctx->rec, ctx->pos)); ctx->pos += sizeof(uint32_t); if (file_field >= cache->file_fields_count) { /* new field, have to re-read fields header to figure out its size. don't do this if we're compressing. */ if (!cache->locked) { if (mail_cache_header_fields_read(cache) < 0) return -1; } if (file_field >= cache->file_fields_count) { mail_cache_set_corrupted(cache, "field index too large (%u >= %u)", file_field, cache->file_fields_count); return -1; } /* field reading might have re-mmaped the file and caused rec pointer to break. need to get it again. */ if (mail_cache_get_record(cache, ctx->offset, &ctx->rec) < 0) return -1; ctx->remap_counter = cache->remap_counter; } field_idx = cache->file_field_map[file_field]; data_size = cache->fields[field_idx].field.field_size; if (data_size == UINT_MAX && ctx->pos + sizeof(uint32_t) <= ctx->rec->size) { /* variable size field. get its size from the file. */ data_size = *((const uint32_t *) CONST_PTR_OFFSET(ctx->rec, ctx->pos)); ctx->pos += sizeof(uint32_t); } if (ctx->rec->size - ctx->pos < data_size) { mail_cache_set_corrupted(cache, "record continues outside its allocated size"); return -1; } field_r->field_idx = field_idx; field_r->data = CONST_PTR_OFFSET(ctx->rec, ctx->pos); field_r->size = data_size; field_r->offset = ctx->offset + ctx->pos; /* each record begins from 32bit aligned position */ ctx->pos += (data_size + sizeof(uint32_t)-1) & ~(sizeof(uint32_t)-1); return 1; }