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