mail-index-fsck.c revision a553ee9b9c50d7cd317825ccb8150331560a9dd9
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2004-2008 Dovecot authors, see the included COPYING file */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void mail_index_fsck_error(struct mail_index *index,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void mail_index_fsck_error(struct mail_index *index,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *fmt, ...)
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen mail_index_set_error(index, "Fixed index file %s: %s",
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen mail_index_fsck_error(index, #field" %u -> %u", \
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenmail_index_fsck_log_pos(struct mail_index *index, struct mail_index_map *map,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen mail_transaction_log_get_head(index->log, &file_seq, &file_offset);
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen hdr->log_file_head_offset = hdr->log_file_tail_offset =
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (hdr->log_file_tail_offset > hdr->log_file_head_offset)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen hdr->log_file_tail_offset = hdr->log_file_head_offset;
4ee00532a265bdfb38539d811fcd12d51210ac35Timo Sirainen if (hdr->log_file_seq == map->hdr.log_file_seq) {
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen /* don't bother complaining about these if file changed too */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenmail_index_fsck_header(struct mail_index *index, struct mail_index_map *map,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* mail_index_map_check_header() has already checked that the index
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen isn't completely broken. */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (hdr->uid_validity == 0 && hdr->next_uid != 1)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenarray_has_name(const ARRAY_TYPE(const_string) *names, const char *name)
0db5b158a00c08955bdacc99b1e2cd1ec07f4311Timo Sirainen const char *const *str;
0db5b158a00c08955bdacc99b1e2cd1ec07f4311Timo Sirainen unsigned int i, count;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for (i = 0; i < count; i++) {
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainenstatic unsigned int
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainenmail_index_fsck_find_keyword_count(struct mail_index_map *map,
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen unsigned int r, i, j, cur, max = 0, kw_pos, kw_size;
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen for (r = 0; r < map->rec_map->records_count; r++) {
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen if (kw[i] != 0) {
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen for (j = 0; j < 8; j++) {
407caeb5d0c8a6b158e2caef48dd909011d40340Timo Sirainen rec = CONST_PTR_OFFSET(rec, map->hdr.record_size);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenkeyword_name_is_valid(const char *buffer, unsigned int pos, unsigned int size)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (((unsigned char)buffer[pos] & 0x7f) < 32) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* control characters aren't valid */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_index_fsck_keywords(struct mail_index *index, struct mail_index_map *map,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int ext_offset, unsigned int *offset_p)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const struct mail_index_keyword_header *kw_hdr;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const struct mail_index_keyword_header_rec *kw_rec;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct mail_index_keyword_header_rec new_kw_rec;
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen unsigned int i, j, name_pos, name_size, rec_pos, hdr_offset, diff;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int changed_count, keywords_count, name_base_pos;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_index_map_ext_hdr_offset(sizeof("keywords")-1);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen kw_hdr = CONST_PTR_OFFSET(map->hdr_base, hdr_offset);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen name_buffer = (const char *)(kw_rec + keywords_count);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen name_pos = (size_t)(name_buffer - (const char *)kw_hdr);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* the header is completely broken */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_index_fsck_find_keyword_count(map, ext_hdr);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_index_fsck_error(index, "Assuming keywords_count = %u",
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* create keyword name array. invalid keywords are added as
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen empty strings */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen for (i = 0; i < keywords_count; i++) {
5fbf8719b9ef072295c16bc4492f9f0ece92117dTimo Sirainen !keyword_name_is_valid(name_buffer, kw_rec[i].name_offset,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (*name != '\0' && array_has_name(&names, name)) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* duplicate */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* give new names to invalid keywords */
5fbf8719b9ef072295c16bc4492f9f0ece92117dTimo Sirainen for (i = j = 0; i < keywords_count; i++) {
5fbf8719b9ef072295c16bc4492f9f0ece92117dTimo Sirainen /* nothing was broken */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen mail_index_fsck_error(index, "Renamed %u keywords to unknown-*",
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen new_kw_hdr = buffer_append_space_unsafe(dest, sizeof(*new_kw_hdr));
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* add keyword records so we can start appending names directly */
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen buffer_append_space_unsafe(dest, keywords_count * sizeof(*kw_rec));
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* write the actual records and names */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen for (i = 0; i < keywords_count; i++) {
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen new_kw_rec.name_offset = dest->used - name_base_pos;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen buffer_write(dest, rec_pos, &new_kw_rec, sizeof(new_kw_rec));
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen buffer_append(dest, name_array[i], strlen(name_array[i]) + 1);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* keep the header size at least the same size as before */
9349a0afffad990e45d3ad33081e1d2d9e68a753Timo Sirainen buffer_append_zero(dest, ext_hdr->hdr_size - dest->used);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* need to resize the header */
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen buffer_copy(map->hdr_copy_buf, hdr_offset + diff,
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen i_assert(hdr_offset + dest->used <= map->hdr_copy_buf->used);
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen buffer_write(map->hdr_copy_buf, hdr_offset, dest->data, dest->used);
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen /* keywords changed unexpectedly, so all views are broken now */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_index_fsck_extensions(struct mail_index *index, struct mail_index_map *map,
3e28b527dd6048a40684afd29cff0ee008fc0014Timo Sirainen offset = MAIL_INDEX_HEADER_SIZE_ALIGN(hdr->base_header_size);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* mail_index_map_ext_get_next() uses map->hdr, so make sure
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen it's up-to-date */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (mail_index_map_ext_get_next(map, &next_offset,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* the extension continued outside header, drop it */
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen "Dropped extension #%d (%s) "
ccffb125d94adff0ad776de5a96e22f864d6fb0aTimo Sirainen "with invalid header size",
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (mail_index_map_ext_hdr_check(hdr, ext_hdr, name,
c44f402f17f9a58ead24ac0083945cae86fb172bTimo Sirainen "Dropped broken extension #%d (%s)", i, name);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* name may change if header buffer is changed */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* drop the field */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen buffer_set_used_size(map->hdr_copy_buf, hdr->header_size);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenmail_index_fsck_records(struct mail_index *index, struct mail_index_map *map,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen bool logged_unordered_uids = FALSE, logged_zero_uids = FALSE;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen for (i = 0; i < map->rec_map->records_count; ) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* log an error once, and skip this record */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen "Record UIDs have zeroes");
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen "Record UIDs unordered");
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen /* not the fastest way when we're skipping lots of
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen records, but this should happen rarely so don't
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen bother optimizing. */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* all existing views are broken now */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen mail_index_fsck_error(index, "next_uid %u -> %u",
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen hdr->first_unseen_uid_lowwater = hdr->next_uid;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen hdr->first_deleted_uid_lowwater = hdr->next_uid;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenmail_index_fsck_map(struct mail_index *index, struct mail_index_map *map)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* Remember the log head position. If we go back in the index's
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen head offset, ignore errors in the log up to this offset. */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_warning("fscking index file %s", index->filepath);
f119596e34bc4a7ce374f4aa5f4f1eb12061a372Timo Sirainen /* we're trying to open the index files, but there wasn't
f119596e34bc4a7ce374f4aa5f4f1eb12061a372Timo Sirainen any .log file. this should be rare, so just fsck it without
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (mail_transaction_log_sync_lock(index->log, &file_seq,