| // Copyright 2019 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 <memory> |
| #include <utility> |
| |
| #include <fvm/format.h> |
| #include <zircon/errors.h> |
| #include <zxtest/zxtest.h> |
| |
| namespace fvm { |
| namespace { |
| |
| // 1 MB slice. |
| constexpr uint64_t kSliceSize = 1lu << 20; |
| |
| // 4 GB fvm_partition_size. |
| constexpr uint64_t kPartitionSize = 4lu << 30; |
| |
| struct Metadata { |
| std::unique_ptr<uint8_t[]> superblock; |
| size_t size; |
| size_t capacity; |
| }; |
| |
| Header MakeHeader(size_t part_size, size_t part_table_size, size_t alloc_table_size) { |
| Header superblock; |
| superblock.fvm_partition_size = part_size; |
| superblock.vpartition_table_size = part_table_size; |
| superblock.allocation_table_size = alloc_table_size; |
| superblock.slice_size = kSliceSize; |
| superblock.magic = fvm::kMagic; |
| superblock.version = fvm::kVersion; |
| superblock.generation = 1; |
| fvm_update_hash(&superblock, sizeof(Header)); |
| return superblock; |
| } |
| |
| Metadata CreateSuperblock(uint64_t initial_disk_size, uint64_t maximum_disk_capacity) { |
| fvm::Header header = MakeHeader(initial_disk_size, fvm::PartitionTable::kLength, |
| fvm::AllocationTable::Length(maximum_disk_capacity, kSliceSize)); |
| FormatInfo info = fvm::FormatInfo::FromSuperBlock(header); |
| Metadata metadata; |
| metadata.superblock = std::make_unique<uint8_t[]>(info.metadata_allocated_size()); |
| metadata.size = info.metadata_size(); |
| metadata.capacity = info.metadata_allocated_size(); |
| memset(metadata.superblock.get(), 0, metadata.capacity); |
| memcpy(metadata.superblock.get(), &header, sizeof(Header)); |
| return metadata; |
| } |
| |
| TEST(IntegrityValidationTest, BothHashesAreOkPickLatest) { |
| Metadata metadata_1 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| Metadata metadata_2 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| reinterpret_cast<fvm::Header*>(metadata_2.superblock.get())->generation = |
| reinterpret_cast<fvm::Header*>(metadata_1.superblock.get())->generation + 1; |
| fvm_update_hash(metadata_1.superblock.get(), metadata_1.size); |
| fvm_update_hash(metadata_2.superblock.get(), metadata_2.size); |
| |
| const void* picked_metadata; |
| ASSERT_EQ(metadata_1.size, metadata_2.size); |
| ASSERT_EQ(metadata_1.capacity, metadata_2.capacity); |
| ASSERT_OK(fvm_validate_header(metadata_1.superblock.get(), metadata_2.superblock.get(), |
| metadata_1.capacity, &picked_metadata)); |
| EXPECT_EQ(picked_metadata, metadata_2.superblock.get()); |
| } |
| |
| TEST(IntegrityValidationTest, PrimaryIsOkAndSecondaryIsCorruptedPicksPrimary) { |
| Metadata metadata_1 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| Metadata metadata_2 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| reinterpret_cast<fvm::Header*>(metadata_2.superblock.get())->fvm_partition_size = 0; |
| fvm_update_hash(metadata_1.superblock.get(), metadata_1.size); |
| |
| const void* picked_metadata; |
| ASSERT_EQ(metadata_1.size, metadata_2.size); |
| ASSERT_EQ(metadata_1.capacity, metadata_2.capacity); |
| ASSERT_OK(fvm_validate_header(metadata_1.superblock.get(), metadata_2.superblock.get(), |
| metadata_1.capacity, &picked_metadata)); |
| EXPECT_EQ(picked_metadata, metadata_1.superblock.get()); |
| } |
| |
| TEST(IntegrityValidationTest, PrimaryIsCorruptedAndSecondaryIsOkPicksSecondary) { |
| Metadata metadata_1 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| Metadata metadata_2 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| reinterpret_cast<fvm::Header*>(metadata_1.superblock.get())->fvm_partition_size = 0; |
| fvm_update_hash(metadata_2.superblock.get(), metadata_2.size); |
| |
| const void* picked_metadata; |
| ASSERT_EQ(metadata_1.size, metadata_2.size); |
| ASSERT_EQ(metadata_1.capacity, metadata_2.capacity); |
| ASSERT_OK(fvm_validate_header(metadata_1.superblock.get(), metadata_2.superblock.get(), |
| metadata_1.capacity, &picked_metadata)); |
| EXPECT_EQ(picked_metadata, metadata_2.superblock.get()); |
| } |
| |
| TEST(IntegrityValidationTest, BothAreCorruptedIsBadState) { |
| Metadata metadata_1 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| Metadata metadata_2 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| reinterpret_cast<fvm::Header*>(metadata_1.superblock.get())->fvm_partition_size = 0; |
| reinterpret_cast<fvm::Header*>(metadata_2.superblock.get())->fvm_partition_size = 0; |
| |
| ASSERT_EQ(metadata_1.size, metadata_2.size); |
| ASSERT_EQ(metadata_1.capacity, metadata_2.capacity); |
| ASSERT_EQ(fvm_validate_header(metadata_1.superblock.get(), metadata_2.superblock.get(), |
| metadata_1.capacity, nullptr), |
| ZX_ERR_BAD_STATE); |
| } |
| |
| TEST(IntegrityValidationTest, ReportedMetadataSizeIsTooSmallOnPrimaryPicksSecondary) { |
| Metadata metadata_1 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| Metadata metadata_2 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| reinterpret_cast<fvm::Header*>(metadata_1.superblock.get())->allocation_table_size = 0; |
| fvm_update_hash(metadata_2.superblock.get(), metadata_2.size); |
| |
| const void* picked_metadata; |
| ASSERT_EQ(metadata_1.size, metadata_2.size); |
| ASSERT_EQ(metadata_1.capacity, metadata_2.capacity); |
| ASSERT_OK(fvm_validate_header(metadata_1.superblock.get(), metadata_2.superblock.get(), |
| metadata_1.capacity, &picked_metadata)); |
| EXPECT_EQ(picked_metadata, metadata_2.superblock.get()); |
| } |
| |
| TEST(IntegrityValidationTest, ReportedMetadataSizeIsTooSmallOnSecondaryPicksPrimary) { |
| Metadata metadata_1 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| Metadata metadata_2 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| reinterpret_cast<fvm::Header*>(metadata_2.superblock.get())->allocation_table_size = 0; |
| fvm_update_hash(metadata_1.superblock.get(), metadata_1.size); |
| |
| const void* picked_metadata; |
| ASSERT_EQ(metadata_1.size, metadata_2.size); |
| ASSERT_EQ(metadata_1.capacity, metadata_2.capacity); |
| ASSERT_OK(fvm_validate_header(metadata_1.superblock.get(), metadata_2.superblock.get(), |
| metadata_1.capacity, &picked_metadata)); |
| EXPECT_EQ(picked_metadata, metadata_1.superblock.get()); |
| } |
| |
| TEST(IntegrityValidationTest, ReportedMetadataSizeIsTooSmallOnBothIsBadState) { |
| Metadata metadata_1 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| Metadata metadata_2 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| reinterpret_cast<fvm::Header*>(metadata_1.superblock.get())->allocation_table_size = 0; |
| reinterpret_cast<fvm::Header*>(metadata_2.superblock.get())->allocation_table_size = 0; |
| |
| ASSERT_EQ(metadata_1.size, metadata_2.size); |
| ASSERT_EQ(metadata_1.capacity, metadata_2.capacity); |
| ASSERT_EQ(fvm_validate_header(metadata_1.superblock.get(), metadata_2.superblock.get(), |
| metadata_1.capacity, nullptr), |
| ZX_ERR_BAD_STATE); |
| } |
| |
| TEST(IntegrityValidationTest, ValidatesMetadataSizeNotCapacity) { |
| Metadata metadata_1 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| Metadata metadata_2 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| fvm_update_hash(metadata_1.superblock.get(), metadata_1.size); |
| // This is not taken into account when validating the metadata header, we only check the data |
| // we are actually using. |
| memset(metadata_1.superblock.get() + metadata_1.size, 1, metadata_1.capacity - metadata_1.size); |
| |
| const void* picked_metadata; |
| ASSERT_EQ(metadata_1.size, metadata_2.size); |
| ASSERT_EQ(metadata_1.capacity, metadata_2.capacity); |
| ASSERT_OK(fvm_validate_header(metadata_1.superblock.get(), metadata_2.superblock.get(), |
| metadata_1.capacity, &picked_metadata)); |
| EXPECT_EQ(picked_metadata, metadata_1.superblock.get()); |
| } |
| |
| TEST(IntegrityValidationTest, ZeroedHeaderIsBadState) { |
| Metadata metadata_1 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| memset(metadata_1.superblock.get(), 0, metadata_1.capacity); |
| |
| Metadata metadata_2 = CreateSuperblock(kPartitionSize, 2 * kPartitionSize); |
| memset(metadata_2.superblock.get(), 0, metadata_2.capacity); |
| |
| ASSERT_EQ(fvm_validate_header(metadata_1.superblock.get(), metadata_2.superblock.get(), |
| metadata_1.capacity, nullptr), |
| ZX_ERR_BAD_STATE); |
| } |
| |
| } // namespace |
| } // namespace fvm |