blob: 80dfd2772a6411c1648da41f30595721dd81526e [file] [log] [blame]
// Copyright 2018 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 "garnet/lib/machina/qcow.h"
#include <stdlib.h>
#include <unistd.h>
#include "gtest/gtest.h"
#include "lib/fxl/logging.h"
namespace machina {
namespace {
static constexpr size_t kClusterBits = 16;
static constexpr uint64_t kClusterSize = 1u << kClusterBits;
static constexpr uint64_t ClusterOffset(uint64_t cluster) {
return cluster * kClusterSize;
}
// Allocate L1 table on cluster 1, L2 tables immediately following, then
// refcount tables and finally data clusters.
//
// Note we add at least one empty cluster between adjacent structures to verify
// we don't overrun any clusters.
static constexpr uint64_t kL1TableOffset = ClusterOffset(1);
static constexpr uint64_t kL2TableClusterOffsets[] = {
ClusterOffset(3),
ClusterOffset(5),
ClusterOffset(7),
ClusterOffset(9),
};
static constexpr uint64_t kRefcountTableOffset = ClusterOffset(11);
static constexpr uint64_t kRefcountBlockClusterOffsets[] = {
ClusterOffset(13),
ClusterOffset(15),
ClusterOffset(17),
ClusterOffset(19),
};
static constexpr uint64_t kFirstDataCluster = 21;
// These are empty clusters that are skipped when interacting with the file.
// They should not be read from or written to.
static constexpr uint64_t kPaddingClusterOffsets[] = {
// clang-format off
ClusterOffset(2),
ClusterOffset(4),
ClusterOffset(6),
ClusterOffset(8),
ClusterOffset(10),
ClusterOffset(12),
ClusterOffset(14),
ClusterOffset(16),
ClusterOffset(18),
// clang-format on
};
static constexpr uint8_t kZeroCluster[kClusterSize] = {};
static constexpr QcowHeader kDefaultHeaderV2 = {
.magic = kQcowMagic,
.version = 2,
.backing_file_offset = 0,
.backing_file_size = 0,
.cluster_bits = kClusterBits,
.size = 4ul * 1024 * 1024 * 1024,
.crypt_method = 0,
.l1_size = 4,
.l1_table_offset = kL1TableOffset,
.refcount_table_offset = kRefcountTableOffset,
.refcount_table_clusters = 1,
.nb_snapshots = 0,
.snapshots_offset = 0,
.incompatible_features = 0,
.compatible_features = 0,
.autoclear_features = 0,
.refcount_order = 0,
.header_length = 0,
};
static constexpr QcowHeader kDefaultHeaderV3 = {
.magic = kQcowMagic,
.version = 3,
.backing_file_offset = 0,
.backing_file_size = 0,
.cluster_bits = kClusterBits,
.size = 4ul * 1024 * 1024 * 1024,
.crypt_method = 0,
.l1_size = 4,
.l1_table_offset = kL1TableOffset,
.refcount_table_offset = kRefcountTableOffset,
.refcount_table_clusters = 1,
.nb_snapshots = 0,
.snapshots_offset = 0,
.incompatible_features = 0,
.compatible_features = 0,
.autoclear_features = 0,
.refcount_order = 4,
.header_length = sizeof(QcowHeader),
};
class QcowTest : public testing::Test {
public:
QcowTest() {}
void SetUp() override {
fd_.reset(mkstemp(&path_[0]));
ASSERT_TRUE(fd_);
}
void TearDown() override { VerifyPaddingClustersAreEmpty(); }
void VerifyPaddingClustersAreEmpty() {
uint8_t cluster[kClusterSize];
for (size_t i = 0; i < countof(kPaddingClusterOffsets); ++i) {
SeekTo(kPaddingClusterOffsets[i]);
ASSERT_EQ(read(fd_.get(), cluster, kClusterSize),
static_cast<int>(kClusterSize));
ASSERT_EQ(memcmp(cluster, kZeroCluster, kClusterSize), 0);
}
}
void WriteQcowHeader(const QcowHeader& header) {
header_ = header;
QcowHeader be_header = header.HostToBigEndian();
SeekTo(0);
Write(&be_header);
WriteL1Table();
WriteRefcountTable();
}
void WriteL1Table() {
// Convert l1 entries to big-endian
uint64_t be_table[countof(kL2TableClusterOffsets)];
for (size_t i = 0; i < countof(kL2TableClusterOffsets); ++i) {
be_table[i] = HostToBigEndianTraits::Convert(kL2TableClusterOffsets[i]);
}
// Write L1 table.
SeekTo(header_.l1_table_offset);
Write(be_table, countof(kL2TableClusterOffsets));
// Initialize empty L2 tables.
for (size_t i = 0; i < countof(kL2TableClusterOffsets); ++i) {
SeekTo(kL2TableClusterOffsets[i]);
Write(kZeroCluster, sizeof(kZeroCluster));
}
}
void WriteRefcountTable() {
// Convert entries to big-endian
uint64_t be_table[countof(kRefcountBlockClusterOffsets)];
for (size_t i = 0; i < countof(kRefcountBlockClusterOffsets); ++i) {
be_table[i] =
HostToBigEndianTraits::Convert(kRefcountBlockClusterOffsets[i]);
}
// Write refcount table
SeekTo(header_.refcount_table_offset);
Write(be_table, countof(kRefcountBlockClusterOffsets));
// Initialize empty refcount blocks.
for (size_t i = 0; i < countof(kRefcountBlockClusterOffsets); ++i) {
SeekTo(kRefcountBlockClusterOffsets[i]);
Write(kZeroCluster, sizeof(kZeroCluster));
}
}
void SeekTo(off_t offset) {
ASSERT_EQ(lseek(fd_.get(), offset, SEEK_SET), offset);
}
template <typename T>
void Write(const T* ptr) {
ASSERT_EQ(write(fd_.get(), ptr, sizeof(T)),
static_cast<ssize_t>(sizeof(T)));
}
// Writes an array of T values at the current file location.
template <typename T>
void Write(const T* ptr, size_t len) {
ASSERT_EQ(write(fd_.get(), ptr, len * sizeof(T)),
static_cast<ssize_t>(len * sizeof(T)));
}
uint64_t ReadRefcount(QcowRefcount* refcount_table, uint32_t cluster) {
uint64_t refcount;
EXPECT_EQ(ZX_OK, refcount_table->ReadRefcount(cluster, &refcount));
return refcount;
}
protected:
std::string path_ = "/tmp/qcow-test.XXXXXX";
fbl::unique_fd fd_;
QcowHeader header_;
QcowFile file_;
};
TEST_F(QcowTest, V2Load) {
WriteQcowHeader(kDefaultHeaderV2);
ASSERT_EQ(file_.Load(fd_.get()), ZX_OK);
}
TEST_F(QcowTest, V2IgnoreExtendedAttributes) {
// Write some values to the fields that do not exist with QCOW2 files.
QcowHeader header = kDefaultHeaderV2;
header.incompatible_features = 0xff;
header.compatible_features = 0xff;
header.autoclear_features = 0xff;
header.refcount_order = 0xff;
header.header_length = 0xff;
WriteQcowHeader(header);
// Load and validate the QCOW2 defaults are used.
ASSERT_EQ(file_.Load(fd_.get()), ZX_OK);
EXPECT_EQ(file_.header().incompatible_features, 0u);
EXPECT_EQ(file_.header().compatible_features, 0u);
EXPECT_EQ(file_.header().autoclear_features, 0u);
EXPECT_EQ(file_.header().refcount_order, 4u);
EXPECT_EQ(file_.header().header_length, 72u);
}
TEST_F(QcowTest, V3Load) {
WriteQcowHeader(kDefaultHeaderV3);
ASSERT_EQ(file_.Load(fd_.get()), ZX_OK);
}
TEST_F(QcowTest, V3RejectIncompatibleFeatures) {
QcowHeader header = kDefaultHeaderV3;
header.incompatible_features = 1;
WriteQcowHeader(header);
ASSERT_EQ(file_.Load(fd_.get()), ZX_ERR_NOT_SUPPORTED);
}
TEST_F(QcowTest, V3RejectCryptMethod) {
QcowHeader header = kDefaultHeaderV3;
header.crypt_method = 1;
WriteQcowHeader(header);
ASSERT_EQ(file_.Load(fd_.get()), ZX_ERR_NOT_SUPPORTED);
}
TEST_F(QcowTest, ReadUnmappedCluster) {
WriteQcowHeader(kDefaultHeaderV2);
// The cluster is not mapped. Verify that reads return all 0's.
uint8_t result[kClusterSize];
memset(result, 0xff, sizeof(result));
uint8_t expected[kClusterSize] = {};
ASSERT_EQ(file_.Load(fd_.get()), ZX_OK);
ASSERT_EQ(file_.Read(0, result, sizeof(result)), ZX_OK);
ASSERT_EQ(memcmp(result, expected, sizeof(result)), 0);
}
TEST_F(QcowTest, ReadMappedCluster) {
WriteQcowHeader(kDefaultHeaderV2);
// Write L2 entry
uint64_t l2_offset = kL2TableClusterOffsets[0];
uint64_t data_cluster_offset = ClusterOffset(kFirstDataCluster);
uint64_t l2_entry = HostToBigEndianTraits::Convert(data_cluster_offset);
SeekTo(l2_offset);
Write(&l2_entry);
// Write data to cluster.
uint8_t cluster_data[kClusterSize];
memset(cluster_data, 0xab, sizeof(cluster_data));
SeekTo(data_cluster_offset);
Write(cluster_data, kClusterSize);
// Read cluster.
uint8_t result[kClusterSize];
ASSERT_EQ(file_.Load(fd_.get()), ZX_OK);
ASSERT_EQ(file_.Read(0, result, sizeof(result)), ZX_OK);
ASSERT_EQ(memcmp(result, cluster_data, sizeof(result)), 0);
}
TEST_F(QcowTest, RejectCompressedCluster) {
WriteQcowHeader(kDefaultHeaderV2);
// Write L2 entry
uint64_t l2_offset = kL2TableClusterOffsets[0];
uint64_t data_cluster_offset = ClusterOffset(kFirstDataCluster);
uint64_t l2_entry = HostToBigEndianTraits::Convert(data_cluster_offset |
kTableEntryCompressedBit);
SeekTo(l2_offset);
Write(&l2_entry);
// Write data to cluster.
uint8_t cluster_data[kClusterSize];
memset(cluster_data, 0xab, sizeof(cluster_data));
SeekTo(data_cluster_offset);
Write(cluster_data, kClusterSize);
// Attempt to read compressed cluster.
ASSERT_EQ(file_.Load(fd_.get()), ZX_OK);
ASSERT_EQ(file_.Read(0, cluster_data, sizeof(cluster_data)),
ZX_ERR_NOT_SUPPORTED);
}
TEST_F(QcowTest, ReadWriteRefcountOrder0) {
QcowHeader header = kDefaultHeaderV3;
header.refcount_order = 0;
WriteQcowHeader(header);
// 1 bit refcount fields.
uint64_t refcount_block_offset = kRefcountBlockClusterOffsets[0];
uint32_t refcount_block[] = {
0xf0f0f0f0,
0x0f0f0f0f,
};
SeekTo(refcount_block_offset);
Write(refcount_block, countof(refcount_block));
// Read refcount.
ASSERT_EQ(file_.Load(fd_.get()), ZX_OK);
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 0));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 1));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 2));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 3));
EXPECT_EQ(1u, ReadRefcount(file_.refcount_table(), 4));
EXPECT_EQ(1u, ReadRefcount(file_.refcount_table(), 5));
EXPECT_EQ(1u, ReadRefcount(file_.refcount_table(), 6));
EXPECT_EQ(1u, ReadRefcount(file_.refcount_table(), 7));
EXPECT_EQ(1u, ReadRefcount(file_.refcount_table(), 32));
EXPECT_EQ(1u, ReadRefcount(file_.refcount_table(), 33));
EXPECT_EQ(1u, ReadRefcount(file_.refcount_table(), 34));
EXPECT_EQ(1u, ReadRefcount(file_.refcount_table(), 35));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 36));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 37));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 38));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 39));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(0, 1));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(1, 0));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(2, 1));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(3, 0));
EXPECT_EQ(1u, ReadRefcount(file_.refcount_table(), 0));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 1));
EXPECT_EQ(1u, ReadRefcount(file_.refcount_table(), 2));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 3));
}
TEST_F(QcowTest, ReadWriteRefcountOrder1) {
QcowHeader header = kDefaultHeaderV3;
header.refcount_order = 1;
WriteQcowHeader(header);
// 2 bit refcount fields.
uint64_t refcount_block_offset = kRefcountBlockClusterOffsets[0];
uint32_t refcount_block[] = {
0xf0f0f0f0,
0x0f0f0f0f,
};
SeekTo(refcount_block_offset);
Write(refcount_block, countof(refcount_block));
// Read refcount.
ASSERT_EQ(file_.Load(fd_.get()), ZX_OK);
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 0));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 1));
EXPECT_EQ(3u, ReadRefcount(file_.refcount_table(), 2));
EXPECT_EQ(3u, ReadRefcount(file_.refcount_table(), 3));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 4));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 5));
EXPECT_EQ(3u, ReadRefcount(file_.refcount_table(), 6));
EXPECT_EQ(3u, ReadRefcount(file_.refcount_table(), 7));
EXPECT_EQ(3u, ReadRefcount(file_.refcount_table(), 16));
EXPECT_EQ(3u, ReadRefcount(file_.refcount_table(), 17));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 18));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 19));
EXPECT_EQ(3u, ReadRefcount(file_.refcount_table(), 20));
EXPECT_EQ(3u, ReadRefcount(file_.refcount_table(), 21));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 22));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 23));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(0, 1));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(1, 2));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(2, 3));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(3, 0));
EXPECT_EQ(1u, ReadRefcount(file_.refcount_table(), 0));
EXPECT_EQ(2u, ReadRefcount(file_.refcount_table(), 1));
EXPECT_EQ(3u, ReadRefcount(file_.refcount_table(), 2));
EXPECT_EQ(0u, ReadRefcount(file_.refcount_table(), 3));
}
TEST_F(QcowTest, ReadWriteRefcountOrder2) {
QcowHeader header = kDefaultHeaderV3;
header.refcount_order = 2;
WriteQcowHeader(header);
// 4 bit refcount fields.
uint64_t refcount_block_offset = kRefcountBlockClusterOffsets[0];
uint32_t refcount_block[] = {
0x76543210,
0xfedcba98,
};
SeekTo(refcount_block_offset);
Write(refcount_block, countof(refcount_block));
// Read refcount.
ASSERT_EQ(file_.Load(fd_.get()), ZX_OK);
EXPECT_EQ(0x0u, ReadRefcount(file_.refcount_table(), 0));
EXPECT_EQ(0x1u, ReadRefcount(file_.refcount_table(), 1));
EXPECT_EQ(0x2u, ReadRefcount(file_.refcount_table(), 2));
EXPECT_EQ(0x3u, ReadRefcount(file_.refcount_table(), 3));
EXPECT_EQ(0x4u, ReadRefcount(file_.refcount_table(), 4));
EXPECT_EQ(0x5u, ReadRefcount(file_.refcount_table(), 5));
EXPECT_EQ(0x6u, ReadRefcount(file_.refcount_table(), 6));
EXPECT_EQ(0x7u, ReadRefcount(file_.refcount_table(), 7));
EXPECT_EQ(0x8u, ReadRefcount(file_.refcount_table(), 8));
EXPECT_EQ(0x9u, ReadRefcount(file_.refcount_table(), 9));
EXPECT_EQ(0xau, ReadRefcount(file_.refcount_table(), 10));
EXPECT_EQ(0xbu, ReadRefcount(file_.refcount_table(), 11));
EXPECT_EQ(0xcu, ReadRefcount(file_.refcount_table(), 12));
EXPECT_EQ(0xdu, ReadRefcount(file_.refcount_table(), 13));
EXPECT_EQ(0xeu, ReadRefcount(file_.refcount_table(), 14));
EXPECT_EQ(0xfu, ReadRefcount(file_.refcount_table(), 15));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(0, 0xa));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(1, 0xb));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(2, 0x7));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(3, 0x6));
EXPECT_EQ(0xau, ReadRefcount(file_.refcount_table(), 0));
EXPECT_EQ(0xbu, ReadRefcount(file_.refcount_table(), 1));
EXPECT_EQ(0x7u, ReadRefcount(file_.refcount_table(), 2));
EXPECT_EQ(0x6u, ReadRefcount(file_.refcount_table(), 3));
}
TEST_F(QcowTest, ReadWriteRefcountOrder3) {
QcowHeader header = kDefaultHeaderV3;
header.refcount_order = 3;
WriteQcowHeader(header);
// 8 bit refcount fields.
uint64_t refcount_block_offset = kRefcountBlockClusterOffsets[0];
uint32_t refcount_block[] = {
0x33221100,
0x77665544,
0xbbaa9988,
0xffeeddcc,
};
SeekTo(refcount_block_offset);
Write(refcount_block, countof(refcount_block));
// Read refcount.
ASSERT_EQ(file_.Load(fd_.get()), ZX_OK);
EXPECT_EQ(0x00u, ReadRefcount(file_.refcount_table(), 0));
EXPECT_EQ(0x11u, ReadRefcount(file_.refcount_table(), 1));
EXPECT_EQ(0x22u, ReadRefcount(file_.refcount_table(), 2));
EXPECT_EQ(0x33u, ReadRefcount(file_.refcount_table(), 3));
EXPECT_EQ(0x44u, ReadRefcount(file_.refcount_table(), 4));
EXPECT_EQ(0x55u, ReadRefcount(file_.refcount_table(), 5));
EXPECT_EQ(0x66u, ReadRefcount(file_.refcount_table(), 6));
EXPECT_EQ(0x77u, ReadRefcount(file_.refcount_table(), 7));
EXPECT_EQ(0x88u, ReadRefcount(file_.refcount_table(), 8));
EXPECT_EQ(0x99u, ReadRefcount(file_.refcount_table(), 9));
EXPECT_EQ(0xaau, ReadRefcount(file_.refcount_table(), 10));
EXPECT_EQ(0xbbu, ReadRefcount(file_.refcount_table(), 11));
EXPECT_EQ(0xccu, ReadRefcount(file_.refcount_table(), 12));
EXPECT_EQ(0xddu, ReadRefcount(file_.refcount_table(), 13));
EXPECT_EQ(0xeeu, ReadRefcount(file_.refcount_table(), 14));
EXPECT_EQ(0xffu, ReadRefcount(file_.refcount_table(), 15));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(0, 0xfe));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(1, 0xed));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(2, 0x12));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(3, 0x34));
EXPECT_EQ(0xfeu, ReadRefcount(file_.refcount_table(), 0));
EXPECT_EQ(0xedu, ReadRefcount(file_.refcount_table(), 1));
EXPECT_EQ(0x12u, ReadRefcount(file_.refcount_table(), 2));
EXPECT_EQ(0x34u, ReadRefcount(file_.refcount_table(), 3));
}
TEST_F(QcowTest, ReadWriteRefcountOrder4) {
QcowHeader header = kDefaultHeaderV3;
header.refcount_order = 4;
WriteQcowHeader(header);
// 16 bit refcount fields.
uint64_t refcount_block_offset = kRefcountBlockClusterOffsets[0];
uint16_t refcount_block[] = {
htobe16(0x1234), htobe16(0x5678), htobe16(0x1111), htobe16(0x7654),
htobe16(0x8888), htobe16(0x1212), htobe16(0x2121), htobe16(0x3333),
};
SeekTo(refcount_block_offset);
Write(refcount_block, countof(refcount_block));
// Read refcount.
ASSERT_EQ(file_.Load(fd_.get()), ZX_OK);
EXPECT_EQ(0x1234u, ReadRefcount(file_.refcount_table(), 0));
EXPECT_EQ(0x5678u, ReadRefcount(file_.refcount_table(), 1));
EXPECT_EQ(0x1111u, ReadRefcount(file_.refcount_table(), 2));
EXPECT_EQ(0x7654u, ReadRefcount(file_.refcount_table(), 3));
EXPECT_EQ(0x8888u, ReadRefcount(file_.refcount_table(), 4));
EXPECT_EQ(0x1212u, ReadRefcount(file_.refcount_table(), 5));
EXPECT_EQ(0x2121u, ReadRefcount(file_.refcount_table(), 6));
EXPECT_EQ(0x3333u, ReadRefcount(file_.refcount_table(), 7));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(0, 0xfeed));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(1, 0xd00d));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(2, 0xcafe));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(3, 0xda1e));
EXPECT_EQ(0xfeedu, ReadRefcount(file_.refcount_table(), 0));
EXPECT_EQ(0xd00du, ReadRefcount(file_.refcount_table(), 1));
EXPECT_EQ(0xcafeu, ReadRefcount(file_.refcount_table(), 2));
EXPECT_EQ(0xda1eu, ReadRefcount(file_.refcount_table(), 3));
}
TEST_F(QcowTest, ReadWriteRefcountOrder5) {
QcowHeader header = kDefaultHeaderV3;
header.refcount_order = 5;
WriteQcowHeader(header);
// 32 bit refcount fields.
uint64_t refcount_block_offset = kRefcountBlockClusterOffsets[0];
uint32_t refcount_block[] = {
htobe32(0x01234567), htobe32(0x89abcdef), htobe32(0x11111111),
htobe32(0x76543210), htobe32(0x88888888), htobe32(0x12121212),
htobe32(0x21212121), htobe32(0x33333333),
};
SeekTo(refcount_block_offset);
Write(refcount_block, countof(refcount_block));
// Read refcount.
ASSERT_EQ(file_.Load(fd_.get()), ZX_OK);
EXPECT_EQ(0x01234567u, ReadRefcount(file_.refcount_table(), 0));
EXPECT_EQ(0x89abcdefu, ReadRefcount(file_.refcount_table(), 1));
EXPECT_EQ(0x11111111u, ReadRefcount(file_.refcount_table(), 2));
EXPECT_EQ(0x76543210u, ReadRefcount(file_.refcount_table(), 3));
EXPECT_EQ(0x88888888u, ReadRefcount(file_.refcount_table(), 4));
EXPECT_EQ(0x12121212u, ReadRefcount(file_.refcount_table(), 5));
EXPECT_EQ(0x21212121u, ReadRefcount(file_.refcount_table(), 6));
EXPECT_EQ(0x33333333u, ReadRefcount(file_.refcount_table(), 7));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(0, 0xfeed1234));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(1, 0xd00d5432));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(2, 0xcafe8888));
EXPECT_EQ(ZX_OK, file_.refcount_table()->WriteRefcount(3, 0xda1e2222));
EXPECT_EQ(0xfeed1234u, ReadRefcount(file_.refcount_table(), 0));
EXPECT_EQ(0xd00d5432u, ReadRefcount(file_.refcount_table(), 1));
EXPECT_EQ(0xcafe8888u, ReadRefcount(file_.refcount_table(), 2));
EXPECT_EQ(0xda1e2222u, ReadRefcount(file_.refcount_table(), 3));
}
TEST_F(QcowTest, ReadWriteRefcountOrder6) {
QcowHeader header = kDefaultHeaderV3;
header.refcount_order = 6;
WriteQcowHeader(header);
// 64 bit refcount fields.
uint64_t refcount_block_offset = kRefcountBlockClusterOffsets[0];
uint64_t refcount_block[] = {
htobe64(0x0123456789abcdef), htobe64(0xfedcba9876543210),
htobe64(0x1111111111111111), htobe64(0x7654321076543210),
htobe64(0x8888888888888888), htobe64(0x1212121212121212),
htobe64(0x2121212121212121), htobe64(0x3333333333333333),
};
SeekTo(refcount_block_offset);
Write(refcount_block, countof(refcount_block));
// Read refcount.
ASSERT_EQ(file_.Load(fd_.get()), ZX_OK);
EXPECT_EQ(0x0123456789abcdefu, ReadRefcount(file_.refcount_table(), 0));
EXPECT_EQ(0xfedcba9876543210u, ReadRefcount(file_.refcount_table(), 1));
EXPECT_EQ(0x1111111111111111u, ReadRefcount(file_.refcount_table(), 2));
EXPECT_EQ(0x7654321076543210u, ReadRefcount(file_.refcount_table(), 3));
EXPECT_EQ(0x8888888888888888u, ReadRefcount(file_.refcount_table(), 4));
EXPECT_EQ(0x1212121212121212u, ReadRefcount(file_.refcount_table(), 5));
EXPECT_EQ(0x2121212121212121u, ReadRefcount(file_.refcount_table(), 6));
EXPECT_EQ(0x3333333333333333u, ReadRefcount(file_.refcount_table(), 7));
EXPECT_EQ(ZX_OK,
file_.refcount_table()->WriteRefcount(0, 0x0123456701234567));
EXPECT_EQ(ZX_OK,
file_.refcount_table()->WriteRefcount(1, 0xaaaaaaaaaaaaaaaa));
EXPECT_EQ(ZX_OK,
file_.refcount_table()->WriteRefcount(2, 0x1231231231231231));
EXPECT_EQ(ZX_OK,
file_.refcount_table()->WriteRefcount(3, 0x0000000000000000));
EXPECT_EQ(0x0123456701234567u, ReadRefcount(file_.refcount_table(), 0));
EXPECT_EQ(0xaaaaaaaaaaaaaaaau, ReadRefcount(file_.refcount_table(), 1));
EXPECT_EQ(0x1231231231231231u, ReadRefcount(file_.refcount_table(), 2));
EXPECT_EQ(0x0000000000000000u, ReadRefcount(file_.refcount_table(), 3));
}
} // namespace
} // namespace machina