static int
mail_transaction_log_file_read(struct mail_transaction_log_file *file,
			       uoff_t start_offset, bool nfs_flush)
{
	int ret;

	i_assert(file->mmap_base == NULL);

	/* NFS: if file isn't locked, we're optimistic that we can read enough
	   data without flushing attribute cache. if after reading we notice
	   that we really should have read more, flush the cache and try again.
	   if file is locked, the attribute cache was already flushed when
	   refreshing the log. */
	if (file->log->nfs_flush && nfs_flush) {
		if (!file->locked)
			nfs_flush_attr_cache_unlocked(file->filepath);
		else
			nfs_flush_attr_cache_fd_locked(file->filepath, file->fd);
	}

	if (file->buffer != NULL && file->buffer_offset > start_offset) {
		/* we have to insert missing data to beginning of buffer */
		ret = mail_transaction_log_file_insert_read(file, start_offset);
		if (ret <= 0)
			return ret;
	}

	if (file->buffer == NULL) {
		file->buffer =
			buffer_create_dynamic(default_pool, LOG_PREFETCH);
		file->buffer_offset = start_offset;
	}

	if ((ret = mail_transaction_log_file_read_more(file)) <= 0)
		;
	else if (file->log->nfs_flush && !nfs_flush &&
		 mail_transaction_log_file_need_nfs_flush(file)) {
		/* we didn't read enough data. flush and try again. */
		return mail_transaction_log_file_read(file, start_offset, TRUE);
	} else if ((ret = mail_transaction_log_file_sync(file)) <= 0) {
		i_assert(ret != 0); /* ret=0 happens only with mmap */
	} else {
		i_assert(file->sync_offset >= file->buffer_offset);
	}
	buffer_set_used_size(file->buffer,
			     file->sync_offset - file->buffer_offset);
	return ret;
}
int mail_transaction_log_file_map(struct mail_transaction_log_file *file,
				  uoff_t start_offset, uoff_t end_offset)
{
	struct mail_index *index = file->log->index;
	uoff_t map_start_offset = start_offset;
	size_t size;
	int ret;

	if (file->hdr.indexid == 0) {
		/* corrupted */
		return 0;
	}

	i_assert(start_offset >= file->hdr.hdr_size);
	i_assert(start_offset <= end_offset);
	i_assert(file->buffer == NULL || file->mmap_base != NULL ||
		 file->sync_offset >= file->buffer_offset + file->buffer->used);

	if (file->locked_sync_offset_updated && file == file->log->head &&
	    end_offset == (uoff_t)-1) {
		/* we're not interested of going further than sync_offset */
		if (log_file_map_check_offsets(file, start_offset,
					       end_offset) == 0)
			return 0;
		i_assert(start_offset <= file->sync_offset);
		end_offset = file->sync_offset;
	}

	if (file->buffer != NULL && file->buffer_offset <= start_offset) {
		/* see if we already have it */
		size = file->buffer->used;
		if (file->buffer_offset + size >= end_offset)
			return 1;
	}

	if (file->locked) {
		/* set this only when we've synced to end of file while locked
		   (either end_offset=(uoff_t)-1 or we had to read anyway) */
		file->locked_sync_offset_updated = TRUE;
	}

	if (MAIL_TRANSACTION_LOG_FILE_IN_MEMORY(file)) {
		if (start_offset < file->buffer_offset || file->buffer == NULL) {
			/* we had moved the log to memory but failed to read
			   the beginning of the log file */
			mail_index_set_error(index,
				"%s: Beginning of the log isn't available",
				file->filepath);
			return 0;
		}
		return log_file_map_check_offsets(file, start_offset,
						  end_offset);
	}

	if (start_offset > file->sync_offset)
		mail_transaction_log_file_skip_to_head(file);
	if (start_offset > file->sync_offset) {
		/* although we could just skip over the unwanted data, we have
		   to sync everything so that modseqs are calculated
		   correctly */
		map_start_offset = file->sync_offset;
	}

	if ((file->log->index->flags & MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE) == 0)
		ret = mail_transaction_log_file_map_mmap(file, map_start_offset);
	else {
		mail_transaction_log_file_munmap(file);
		ret = mail_transaction_log_file_read(file, map_start_offset, FALSE);
	}

	i_assert(file->buffer == NULL || file->mmap_base != NULL ||
		 file->sync_offset >= file->buffer_offset + file->buffer->used);
	if (ret <= 0)
		return ret;

	i_assert(file->buffer != NULL);
	return log_file_map_check_offsets(file, start_offset, end_offset);
}