| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/storage/fvm/snapshot_metadata_format.h" |
| |
| #include <zircon/assert.h> |
| |
| #include <algorithm> |
| #include <sstream> |
| #include <string> |
| |
| #include <range/range.h> |
| |
| namespace fvm { |
| |
| SnapshotMetadataHeader::SnapshotMetadataHeader() |
| : SnapshotMetadataHeader(kSnapshotMetadataHeaderMinPartitions, |
| kSnapshotMetadataHeaderMinExtentTypes) {} |
| |
| SnapshotMetadataHeader::SnapshotMetadataHeader(uint32_t partition_state_table_entries, |
| uint32_t extent_type_table_entries) { |
| partition_state_table_entry_count = |
| std::clamp(partition_state_table_entries, kSnapshotMetadataHeaderMinPartitions, |
| kSnapshotMetadataHeaderMaxPartitions); |
| extent_type_table_entry_count = |
| std::clamp(extent_type_table_entries, kSnapshotMetadataHeaderMinExtentTypes, |
| kSnapshotMetadataHeaderMaxExtentTypes); |
| |
| // Partition state table can go at a fixed offset after the header. |
| partition_state_table_offset = kSnapshotMetadataHeaderMaxSize; |
| |
| // Extent type table can go immediately after. |
| extent_type_table_offset = PartitionStateTableOffset() + PartitionStateTableSizeBytes(); |
| |
| ZX_ASSERT(PartitionStateTableOffset() + PartitionStateTableSizeBytes() <= |
| kSnapshotMetadataSecondHeaderOffset); |
| ZX_ASSERT(ExtentTypeTableOffset() + ExtentTypeTableSizeBytes() <= |
| kSnapshotMetadataSecondHeaderOffset); |
| } |
| |
| bool SnapshotMetadataHeader::IsValid(std::string& out_error) const { |
| if (magic != kSnapshotMetadataMagic) { |
| out_error = "fvm snapshot magic invalid"; |
| return false; |
| } |
| |
| if (format_version > kSnapshotMetadataCurrentFormatVersion) { |
| out_error = "fvm snapshot metadata version does not match fvm driver (=" + |
| std::to_string(kSnapshotMetadataCurrentFormatVersion) + ")\n" + ToString(); |
| return false; |
| } |
| |
| if (partition_state_table_entry_count < kSnapshotMetadataHeaderMinPartitions || |
| partition_state_table_entry_count > kSnapshotMetadataHeaderMaxPartitions) { |
| out_error = "invalid partition state table sz (" + |
| std::to_string(partition_state_table_entry_count) + ")\n" + ToString(); |
| return false; |
| } |
| |
| if (extent_type_table_entry_count < kSnapshotMetadataHeaderMinExtentTypes || |
| extent_type_table_entry_count > kSnapshotMetadataHeaderMaxExtentTypes) { |
| out_error = "invalid extent type table sz (" + std::to_string(extent_type_table_entry_count) + |
| ")\n" + ToString(); |
| return false; |
| } |
| |
| range::Range<size_t> header_range( |
| HeaderOffset(SnapshotMetadataCopy::kPrimary), |
| HeaderOffset(SnapshotMetadataCopy::kPrimary) + kSnapshotMetadataHeaderMaxSize); |
| range::Range<size_t> second_header_range( |
| HeaderOffset(SnapshotMetadataCopy::kSecondary), |
| HeaderOffset(SnapshotMetadataCopy::kSecondary) + kSnapshotMetadataHeaderMaxSize); |
| range::Range<size_t> part_state_table_range( |
| PartitionStateTableOffset(), PartitionStateTableOffset() + PartitionStateTableSizeBytes()); |
| range::Range<size_t> extent_type_table_range( |
| ExtentTypeTableOffset(), ExtentTypeTableOffset() + ExtentTypeTableSizeBytes()); |
| const std::array<const range::Range<size_t>*, 4> ranges{ |
| &header_range, |
| &second_header_range, |
| &part_state_table_range, |
| &extent_type_table_range, |
| }; |
| for (const auto* range1 : ranges) { |
| for (const auto* range2 : ranges) { |
| if (range1 == range2) |
| continue; |
| if (range::Overlap(*range1, *range2)) { |
| out_error = "Metadata regions overlap\n" + ToString(); |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| size_t SnapshotMetadataHeader::PartitionStateTableSizeBytes() const { |
| return partition_state_table_entry_count * sizeof(PartitionSnapshotState); |
| } |
| |
| size_t SnapshotMetadataHeader::ExtentTypeTableSizeBytes() const { |
| return extent_type_table_entry_count * sizeof(SnapshotExtentType); |
| } |
| |
| size_t SnapshotMetadataHeader::AllocatedMetadataBytes() const { |
| return HeaderOffset(SnapshotMetadataCopy::kSecondary); |
| } |
| |
| uint64_t SnapshotMetadataHeader::HeaderOffset(SnapshotMetadataCopy copy) { |
| switch (copy) { |
| case SnapshotMetadataCopy::kPrimary: |
| return 0; |
| case SnapshotMetadataCopy::kSecondary: |
| return kSnapshotMetadataSecondHeaderOffset; |
| } |
| } |
| |
| std::string SnapshotMetadataHeader::ToString() const { |
| std::stringstream ss; |
| ss << "FVM Snapshot Metadata Header" << std::endl; |
| ss << " magic: " << magic << std::endl; |
| ss << " partition_state_table_offset: " << partition_state_table_offset << std::endl; |
| ss << " partition_state_table_entry_count: " << partition_state_table_entry_count << std::endl; |
| ss << " extent_type_table_offset: " << extent_type_table_offset << std::endl; |
| ss << " extent_type_table_entry_count: " << extent_type_table_entry_count << std::endl; |
| return ss.str(); |
| } |
| |
| void PartitionSnapshotState::Release() { data = 0; } |
| |
| SnapshotExtentType::SnapshotExtentType(uint64_t vpart, uint64_t vslice_offset, |
| uint64_t extent_length_slices, ExtentType type) |
| : vslice_offset(vslice_offset), |
| extent_length_slices(extent_length_slices), |
| vpartition_index(vpart), |
| type(type) {} |
| |
| bool SnapshotExtentType::IsFree() const { return vpartition_index == 0; } |
| |
| void SnapshotExtentType::Release() { vpartition_index = 0; } |
| |
| } // namespace fvm |