bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenstatic void mail_index_map_copy_hdr(struct mail_index_map *map,
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (hdr->base_header_size < sizeof(map->hdr)) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* header smaller than ours, make a copy so our newer headers
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen won't have garbage in them */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen memcpy(&map->hdr, hdr, hdr->base_header_size);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* FIXME: backwards compatibility, remove later. In case this index is
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen accessed with Dovecot v1.0, avoid recent message counter errors. */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen map->hdr.unused_old_recent_messages_count = 0;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenstatic int mail_index_mmap(struct mail_index_map *map, uoff_t file_size)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen struct mail_index_record_map *rec_map = map->rec_map;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* too large file to map into memory */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen mail_index_set_error(index, "Index file too large: %s",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen rec_map->mmap_base = mmap(NULL, file_size, PROT_READ | PROT_WRITE,
12f0c4396d2d9c02b7d5e070aaf64fed5853e9bfAki Tuomi mail_index_set_syscall_error(index, t_strdup_printf(
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen offsetof(struct mail_index_header, major_version) &&
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen hdr->major_version != MAIL_INDEX_MAJOR_VERSION) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* major version change - handle silently */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (rec_map->mmap_size < MAIL_INDEX_HEADER_MIN_SIZE) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen if (!mail_index_check_header_compat(index, hdr, rec_map->mmap_size, &error)) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* Can't use this file */
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: %s",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (rec_map->mmap_used_size <= rec_map->mmap_size)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen "messages_count too large (%u > %u)",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen rec_map->records = PTR_OFFSET(rec_map->mmap_base, map->hdr.header_size);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenstatic int mail_index_read_header(struct mail_index *index,
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen memset(buf, 0, sizeof(struct mail_index_header));
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* try to read the whole header, but it's not necessarily an error to
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen read less since the older versions of the index format could be
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen smaller. Request reading up to buf_size, but accept if we only got
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen the header. */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen } while (ret > 0 && pos < sizeof(struct mail_index_header));
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainenmail_index_try_read_map(struct mail_index_map *map,
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen uoff_t file_size, bool *retry_r, bool try_retry)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen size_t pos, records_size, initial_buf_pos = 0;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen ret = mail_index_read_header(index, read_buf, sizeof(read_buf), &pos);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (pos > (ssize_t)offsetof(struct mail_index_header, major_version) &&
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen hdr->major_version != MAIL_INDEX_MAJOR_VERSION) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* major version change - handle silently */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (ret >= 0 && pos >= MAIL_INDEX_HEADER_MIN_SIZE &&
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen if (!mail_index_check_header_compat(index, hdr, file_size, &error)) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* Can't use this file */
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen mail_index_set_error(index, "Corrupted index file %s: %s",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* place the base header into memory. */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* @UNSAFE: read the rest of the header into memory */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen data = buffer_append_space_unsafe(map->hdr_copy_buf,
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* header read, read the records now. */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen records_size = (size_t)hdr->messages_count * hdr->record_size;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (file_size - hdr->header_size < records_size ||
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen records_size / hdr->record_size != hdr->messages_count)) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen records_count = (file_size - hdr->header_size) /
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen records_size = (size_t)records_count * hdr->record_size;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen mail_index_set_error(index, "Corrupted index file %s: "
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen "messages_count too large (%u > %u)",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* @UNSAFE */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen buffer_set_used_size(map->rec_map->buffer, 0);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen data = buffer_append_space_unsafe(map->rec_map->buffer,
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen ret = pread_full(index->fd, data, records_size - extra,
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* a new index file was renamed over this one. */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen mail_index_set_syscall_error(index, "pread_full()");
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen "Corrupted index file %s: File too small",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen buffer_get_modifiable_data(map->rec_map->buffer, NULL);
1af0c5d8814c2304d09d8ca844a84f0b9b0c1f61Timo Sirainen i_assert(map->hdr_copy_buf->used == map->hdr.header_size);
b71e08759c02a49d02cdfb28343351ad28fc10b5Timo Sirainenstatic int mail_index_read_map(struct mail_index_map *map, uoff_t file_size)
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen mail_index_sync_lost_handler_t *const *handlerp;
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen unsigned int i;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* notify all "sync lost" handlers */
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen array_foreach(&index->sync_lost_handlers, handlerp)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen for (i = 0;; i++) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen try_retry = i < MAIL_INDEX_ESTALE_RETRY_COUNT;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* fstat() below failed */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* ESTALE - reopen index file */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* the file was lost */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen mail_index_set_syscall_error(index, "open()");
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen mail_index_set_syscall_error(index, "fstat()");
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen/* returns -1 = error, 0 = index files are unusable,
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen 1 = index files are usable or at least repairable */
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainenmail_index_map_latest_file(struct mail_index *index, const char **reason_r)
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen ret = mail_index_reopen_if_changed(index, reason_r);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* the index file is lost/broken. let's hope that we can
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen build it from the transaction log. */
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen if ((index->flags & MAIL_INDEX_OPEN_FLAG_NFS_FLUSH) != 0)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen nfs_flush_attr_cache_fd_locked(index->filepath, index->fd);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen mail_index_set_syscall_error(index, "fstat()");
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* mmaping seems to be slower than just reading the file, so even if
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen mmap isn't disabled don't use it unless the file is large enough */
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen use_mmap = (index->flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) == 0 &&
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen file_size != (uoff_t)-1 && file_size > MAIL_INDEX_MMAP_MIN_SIZE;
b71e08759c02a49d02cdfb28343351ad28fc10b5Timo Sirainen ret = mail_index_read_map(new_map, file_size);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* the index files are unusable */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* make sure the header is ok before using this mapping */
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen ret = mail_index_map_check_header(new_map, &error);
45b0d8d0b97be14d10e3a3c12c169e4b352b2aacTimo Sirainen "Corrupted index file %s: %s",
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (mail_index_map_parse_extensions(new_map) < 0)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen else if (mail_index_map_parse_keywords(new_map) < 0)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* fsck and try again */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* fsck replaced the map */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen index->last_read_log_file_seq = new_map->hdr.log_file_seq;
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* first try updating the existing mapping from transaction log. */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* we're not creating/opening the index.
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen sync this as a view from transaction log. */
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen ret = mail_index_sync_map(&index->map, type, FALSE, "initial mapping");
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* try to open and read the latest index. if it fails, we'll
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen fallback to updating the existing mapping from transaction
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen logs (which we'll also do even if the reopening succeeds).
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if index files are unusable (e.g. major version change)
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen don't even try to use the transaction log. */
6a4bfb2b0bb9f53fb1d4e705bf3948ef4d1ecccbTimo Sirainen ret = mail_index_map_latest_file(index, &reason);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* if we're creating the index file, we don't have any
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (index->log->head != NULL && index->indexid != 0) {
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* and update the map with the latest changes
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen from transaction log */
ca2f3260811c4ccd0afb774d7e13a5bd64d0cc34Timo Sirainen /* we fsck'd the index. try opening again. */
ca2f3260811c4ccd0afb774d7e13a5bd64d0cc34Timo Sirainen ret = mail_index_map_latest_file(index, &reason);
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen /* make sure we don't try to open the file again */
7a896d960824c93e2f04ac21e8eae0f892bf8d20Timo Sirainen if (unlink(index->filepath) < 0 && errno != ENOENT)