liblp: Add unit tests for flashing and readback.
These tests check that various aspects of liblp's on-disk management are functioning as intended. This checks redundancy, metadata slot management, and metadata update and readback. Bug: 79173901 Test: liblp_test gtest Change-Id: Ib780676e0f34f44aa255e8fcfded2ceb71fe3dce
This commit is contained in:
parent
e9619eb6d1
commit
0a186a834e
|
@ -55,6 +55,7 @@ cc_test {
|
|||
],
|
||||
srcs: [
|
||||
"builder_test.cpp",
|
||||
"io_test.cpp",
|
||||
"utility_test.cpp",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -306,3 +306,21 @@ TEST(liblp, ExportInvalidGuid) {
|
|||
unique_ptr<LpMetadata> exported = builder->Export();
|
||||
EXPECT_EQ(exported, nullptr);
|
||||
}
|
||||
|
||||
TEST(liblp, MetadataTooLarge) {
|
||||
static const size_t kDiskSize = 128 * 1024;
|
||||
static const size_t kMetadataSize = 64 * 1024;
|
||||
|
||||
// No space to store metadata + geometry.
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize, kMetadataSize, 1);
|
||||
EXPECT_EQ(builder, nullptr);
|
||||
|
||||
// No space to store metadata + geometry + one free sector.
|
||||
builder = MetadataBuilder::New(kDiskSize + LP_METADATA_GEOMETRY_SIZE * 2, kMetadataSize, 1);
|
||||
EXPECT_EQ(builder, nullptr);
|
||||
|
||||
// Space for metadata + geometry + one free sector.
|
||||
builder = MetadataBuilder::New(kDiskSize + LP_METADATA_GEOMETRY_SIZE * 2 + LP_SECTOR_SIZE,
|
||||
kMetadataSize, 1);
|
||||
EXPECT_NE(builder, nullptr);
|
||||
}
|
||||
|
|
|
@ -29,13 +29,16 @@ namespace fs_mgr {
|
|||
// Read logical partition metadata from its predetermined location on a block
|
||||
// device. If readback fails, we also attempt to load from a backup copy.
|
||||
std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
|
||||
std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
|
||||
|
||||
// Read and validate the logical partition geometry from a block device.
|
||||
bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry);
|
||||
bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
|
||||
|
||||
// Read logical partition metadata from an image file that was created with
|
||||
// WriteToImageFile().
|
||||
std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
|
||||
std::unique_ptr<LpMetadata> ReadFromImageFile(int fd);
|
||||
|
||||
} // namespace fs_mgr
|
||||
} // namespace android
|
||||
|
|
|
@ -42,10 +42,13 @@ enum class SyncMode {
|
|||
// The slot number indicates which metadata slot to use.
|
||||
bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
|
||||
uint32_t slot_number);
|
||||
bool WritePartitionTable(int fd, const LpMetadata& metadata, SyncMode sync_mode,
|
||||
uint32_t slot_number);
|
||||
|
||||
// Helper function to serialize geometry and metadata to a normal file, for
|
||||
// flashing or debugging.
|
||||
bool WriteToImageFile(const char* file, const LpMetadata& metadata);
|
||||
bool WriteToImageFile(int fd, const LpMetadata& metadata);
|
||||
|
||||
} // namespace fs_mgr
|
||||
} // namespace android
|
||||
|
|
|
@ -0,0 +1,395 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/memfd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <liblp/builder.h>
|
||||
#include <liblp/reader.h>
|
||||
#include <liblp/writer.h>
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace android::fs_mgr;
|
||||
using unique_fd = android::base::unique_fd;
|
||||
|
||||
// Our tests assume a 128KiB disk with two 512 byte metadata slots.
|
||||
static const size_t kDiskSize = 131072;
|
||||
static const size_t kMetadataSize = 512;
|
||||
static const size_t kMetadataSlots = 2;
|
||||
static const char* TEST_GUID_BASE = "A799D1D6-669F-41D8-A3F0-EBB7572D830";
|
||||
static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302";
|
||||
|
||||
// Helper function for creating an in-memory file descriptor. This lets us
|
||||
// simulate read/writing logical partition metadata as if we had a block device
|
||||
// for a physical partition.
|
||||
static unique_fd CreateFakeDisk(off_t size) {
|
||||
unique_fd fd(syscall(__NR_memfd_create, "fake_disk", MFD_ALLOW_SEALING));
|
||||
if (fd < 0) {
|
||||
perror("memfd_create");
|
||||
return {};
|
||||
}
|
||||
if (ftruncate(fd, size) < 0) {
|
||||
perror("ftruncate");
|
||||
return {};
|
||||
}
|
||||
// Prevent anything from accidentally growing/shrinking the file, as it
|
||||
// would not be allowed on an actual partition.
|
||||
if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
|
||||
perror("fcntl");
|
||||
return {};
|
||||
}
|
||||
// Write garbage to the "disk" so we can tell what has been zeroed or not.
|
||||
unique_ptr<uint8_t[]> buffer = make_unique<uint8_t[]>(size);
|
||||
memset(buffer.get(), 0xcc, size);
|
||||
if (!android::base::WriteFully(fd, buffer.get(), size)) {
|
||||
return {};
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
// Create a disk of the default size.
|
||||
static unique_fd CreateFakeDisk() {
|
||||
return CreateFakeDisk(kDiskSize);
|
||||
}
|
||||
|
||||
// Create a MetadataBuilder around some default sizes.
|
||||
static unique_ptr<MetadataBuilder> CreateDefaultBuilder() {
|
||||
unique_ptr<MetadataBuilder> builder =
|
||||
MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);
|
||||
return builder;
|
||||
}
|
||||
|
||||
static bool AddDefaultPartitions(MetadataBuilder* builder) {
|
||||
Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_NONE);
|
||||
if (!system) {
|
||||
return false;
|
||||
}
|
||||
return builder->GrowPartition(system, 24 * 1024);
|
||||
}
|
||||
|
||||
// Create a temporary disk and flash it with the default partition setup.
|
||||
static unique_fd CreateFlashedDisk() {
|
||||
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
|
||||
if (!builder || !AddDefaultPartitions(builder.get())) {
|
||||
return {};
|
||||
}
|
||||
unique_fd fd = CreateFakeDisk();
|
||||
if (fd < 0) {
|
||||
return {};
|
||||
}
|
||||
// Export and flash.
|
||||
unique_ptr<LpMetadata> exported = builder->Export();
|
||||
if (!exported) {
|
||||
return {};
|
||||
}
|
||||
if (!WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0)) {
|
||||
return {};
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
// Test that our CreateFakeDisk() function works.
|
||||
TEST(liblp, CreateFakeDisk) {
|
||||
unique_fd fd = CreateFakeDisk();
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
uint64_t size;
|
||||
ASSERT_TRUE(GetDescriptorSize(fd, &size));
|
||||
ASSERT_EQ(size, kDiskSize);
|
||||
}
|
||||
|
||||
// Flashing metadata should not work if the metadata was created for a larger
|
||||
// disk than the destination disk.
|
||||
TEST(liblp, ExportDiskTooSmall) {
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 1024, 512, 2);
|
||||
ASSERT_NE(builder, nullptr);
|
||||
unique_ptr<LpMetadata> exported = builder->Export();
|
||||
ASSERT_NE(exported, nullptr);
|
||||
|
||||
// A larger geometry should fail to flash, since there won't be enough
|
||||
// space to store the logical partition range that was specified.
|
||||
unique_fd fd = CreateFakeDisk();
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
EXPECT_FALSE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
|
||||
}
|
||||
|
||||
// Test the basics of flashing a partition and reading it back.
|
||||
TEST(liblp, FlashAndReadback) {
|
||||
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
|
||||
ASSERT_NE(builder, nullptr);
|
||||
ASSERT_TRUE(AddDefaultPartitions(builder.get()));
|
||||
|
||||
unique_fd fd = CreateFakeDisk();
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
// Export and flash.
|
||||
unique_ptr<LpMetadata> exported = builder->Export();
|
||||
ASSERT_NE(exported, nullptr);
|
||||
ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
|
||||
|
||||
// Read back. Note that some fields are only filled in during
|
||||
// serialization, so exported and imported will not be identical. For
|
||||
// example, table sizes and checksums are computed in WritePartitionTable.
|
||||
// Therefore we check on a field-by-field basis.
|
||||
unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
|
||||
ASSERT_NE(imported, nullptr);
|
||||
|
||||
// Check geometry and header.
|
||||
EXPECT_EQ(exported->geometry.metadata_max_size, imported->geometry.metadata_max_size);
|
||||
EXPECT_EQ(exported->geometry.metadata_slot_count, imported->geometry.metadata_slot_count);
|
||||
EXPECT_EQ(exported->geometry.first_logical_sector, imported->geometry.first_logical_sector);
|
||||
EXPECT_EQ(exported->geometry.last_logical_sector, imported->geometry.last_logical_sector);
|
||||
EXPECT_EQ(exported->header.major_version, imported->header.major_version);
|
||||
EXPECT_EQ(exported->header.minor_version, imported->header.minor_version);
|
||||
EXPECT_EQ(exported->header.header_size, imported->header.header_size);
|
||||
|
||||
// Check partition tables.
|
||||
ASSERT_EQ(exported->partitions.size(), imported->partitions.size());
|
||||
EXPECT_EQ(GetPartitionName(exported->partitions[0]), GetPartitionName(imported->partitions[0]));
|
||||
EXPECT_EQ(GetPartitionGuid(exported->partitions[0]), GetPartitionGuid(imported->partitions[0]));
|
||||
EXPECT_EQ(exported->partitions[0].attributes, imported->partitions[0].attributes);
|
||||
EXPECT_EQ(exported->partitions[0].first_extent_index,
|
||||
imported->partitions[0].first_extent_index);
|
||||
EXPECT_EQ(exported->partitions[0].num_extents, imported->partitions[0].num_extents);
|
||||
|
||||
// Check extent tables.
|
||||
ASSERT_EQ(exported->extents.size(), imported->extents.size());
|
||||
EXPECT_EQ(exported->extents[0].num_sectors, imported->extents[0].num_sectors);
|
||||
EXPECT_EQ(exported->extents[0].target_type, imported->extents[0].target_type);
|
||||
EXPECT_EQ(exported->extents[0].target_data, imported->extents[0].target_data);
|
||||
}
|
||||
|
||||
// Test that we can update metadata slots without disturbing others.
|
||||
TEST(liblp, UpdateAnyMetadataSlot) {
|
||||
unique_fd fd = CreateFlashedDisk();
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
|
||||
ASSERT_NE(imported, nullptr);
|
||||
ASSERT_EQ(imported->partitions.size(), 1);
|
||||
EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
|
||||
|
||||
// Verify that we can't read unwritten metadata.
|
||||
ASSERT_EQ(ReadMetadata(fd, 1), nullptr);
|
||||
|
||||
// Change the name before writing to the next slot.
|
||||
strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
|
||||
ASSERT_TRUE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
|
||||
|
||||
// Read back the original slot, make sure it hasn't changed.
|
||||
imported = ReadMetadata(fd, 0);
|
||||
ASSERT_NE(imported, nullptr);
|
||||
ASSERT_EQ(imported->partitions.size(), 1);
|
||||
EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system");
|
||||
|
||||
// Now read back the new slot, and verify that it has a different name.
|
||||
imported = ReadMetadata(fd, 1);
|
||||
ASSERT_NE(imported, nullptr);
|
||||
ASSERT_EQ(imported->partitions.size(), 1);
|
||||
EXPECT_EQ(GetPartitionName(imported->partitions[0]), "vendor");
|
||||
|
||||
// Verify that we didn't overwrite anything in the logical paritition area.
|
||||
// We expect the disk to be filled with 0xcc on creation so we can read
|
||||
// this back and compare it.
|
||||
char expected[LP_SECTOR_SIZE];
|
||||
memset(expected, 0xcc, sizeof(expected));
|
||||
for (uint64_t i = imported->geometry.first_logical_sector;
|
||||
i <= imported->geometry.last_logical_sector; i++) {
|
||||
char buffer[LP_SECTOR_SIZE];
|
||||
ASSERT_GE(lseek(fd, i * LP_SECTOR_SIZE, SEEK_SET), 0);
|
||||
ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
|
||||
ASSERT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(liblp, InvalidMetadataSlot) {
|
||||
unique_fd fd = CreateFlashedDisk();
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
// Make sure all slots are filled.
|
||||
unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
|
||||
ASSERT_NE(metadata, nullptr);
|
||||
for (uint32_t i = 1; i < kMetadataSlots; i++) {
|
||||
ASSERT_TRUE(WritePartitionTable(fd, *metadata.get(), SyncMode::Update, i));
|
||||
}
|
||||
|
||||
// Verify that we can't read unavailable slots.
|
||||
EXPECT_EQ(ReadMetadata(fd, kMetadataSlots), nullptr);
|
||||
}
|
||||
|
||||
// Test that updating a metadata slot does not allow it to be computed based
|
||||
// on mismatching geometry.
|
||||
TEST(liblp, NoChangingGeometry) {
|
||||
unique_fd fd = CreateFlashedDisk();
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
|
||||
ASSERT_NE(imported, nullptr);
|
||||
ASSERT_TRUE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
|
||||
|
||||
imported->geometry.metadata_max_size += LP_SECTOR_SIZE;
|
||||
ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
|
||||
|
||||
imported = ReadMetadata(fd, 0);
|
||||
ASSERT_NE(imported, nullptr);
|
||||
imported->geometry.metadata_slot_count++;
|
||||
ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
|
||||
|
||||
imported = ReadMetadata(fd, 0);
|
||||
ASSERT_NE(imported, nullptr);
|
||||
imported->geometry.first_logical_sector++;
|
||||
ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
|
||||
|
||||
imported = ReadMetadata(fd, 0);
|
||||
ASSERT_NE(imported, nullptr);
|
||||
imported->geometry.last_logical_sector--;
|
||||
ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
|
||||
}
|
||||
|
||||
// Test that changing one bit of metadata is enough to break the checksum.
|
||||
TEST(liblp, BitFlipGeometry) {
|
||||
unique_fd fd = CreateFlashedDisk();
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
LpMetadataGeometry geometry;
|
||||
ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
|
||||
ASSERT_TRUE(android::base::ReadFully(fd, &geometry, sizeof(geometry)));
|
||||
|
||||
LpMetadataGeometry bad_geometry = geometry;
|
||||
bad_geometry.metadata_slot_count++;
|
||||
ASSERT_TRUE(android::base::WriteFully(fd, &bad_geometry, sizeof(bad_geometry)));
|
||||
|
||||
unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
|
||||
ASSERT_NE(metadata, nullptr);
|
||||
EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
|
||||
}
|
||||
|
||||
TEST(liblp, ReadBackupGeometry) {
|
||||
unique_fd fd = CreateFlashedDisk();
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
char corruption[LP_METADATA_GEOMETRY_SIZE];
|
||||
memset(corruption, 0xff, sizeof(corruption));
|
||||
|
||||
// Corrupt the first 4096 bytes of the disk.
|
||||
ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
|
||||
ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
|
||||
EXPECT_NE(ReadMetadata(fd, 0), nullptr);
|
||||
|
||||
// Corrupt the last 4096 bytes too.
|
||||
ASSERT_GE(lseek(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END), 0);
|
||||
ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
|
||||
EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
|
||||
}
|
||||
|
||||
TEST(liblp, ReadBackupMetadata) {
|
||||
unique_fd fd = CreateFlashedDisk();
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
|
||||
|
||||
char corruption[kMetadataSize];
|
||||
memset(corruption, 0xff, sizeof(corruption));
|
||||
|
||||
ASSERT_GE(lseek(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET), 0);
|
||||
ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
|
||||
EXPECT_NE(ReadMetadata(fd, 0), nullptr);
|
||||
|
||||
off_t offset = LP_METADATA_GEOMETRY_SIZE + kMetadataSize * 2;
|
||||
|
||||
// Corrupt the backup metadata.
|
||||
ASSERT_GE(lseek(fd, -offset, SEEK_END), 0);
|
||||
ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));
|
||||
EXPECT_EQ(ReadMetadata(fd, 0), nullptr);
|
||||
}
|
||||
|
||||
// Test that we don't attempt to write metadata if it would overflow its
|
||||
// reserved space.
|
||||
TEST(liblp, TooManyPartitions) {
|
||||
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
|
||||
ASSERT_NE(builder, nullptr);
|
||||
|
||||
// Compute the maximum number of partitions we can fit in 1024 bytes of metadata.
|
||||
size_t max_partitions = (kMetadataSize - sizeof(LpMetadataHeader)) / sizeof(LpMetadataPartition);
|
||||
EXPECT_LT(max_partitions, 10);
|
||||
|
||||
// Add this number of partitions.
|
||||
Partition* partition = nullptr;
|
||||
for (size_t i = 0; i < max_partitions; i++) {
|
||||
std::string guid = std::string(TEST_GUID) + to_string(i);
|
||||
partition = builder->AddPartition(to_string(i), TEST_GUID, LP_PARTITION_ATTR_NONE);
|
||||
ASSERT_NE(partition, nullptr);
|
||||
}
|
||||
ASSERT_NE(partition, nullptr);
|
||||
// Add one extent to any partition to fill up more space - we're at 508
|
||||
// bytes after this, out of 512.
|
||||
ASSERT_TRUE(builder->GrowPartition(partition, 1024));
|
||||
|
||||
unique_ptr<LpMetadata> exported = builder->Export();
|
||||
ASSERT_NE(exported, nullptr);
|
||||
|
||||
unique_fd fd = CreateFakeDisk();
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
// Check that we are able to write our table.
|
||||
ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
|
||||
ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Update, 1));
|
||||
|
||||
// Check that adding one more partition overflows the metadata allotment.
|
||||
partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
|
||||
EXPECT_NE(partition, nullptr);
|
||||
|
||||
exported = builder->Export();
|
||||
ASSERT_NE(exported, nullptr);
|
||||
|
||||
// The new table should be too large to be written.
|
||||
ASSERT_FALSE(WritePartitionTable(fd, *exported.get(), SyncMode::Update, 1));
|
||||
|
||||
// Check that the first and last logical sectors weren't touched when we
|
||||
// wrote this almost-full metadata.
|
||||
char expected[LP_SECTOR_SIZE];
|
||||
memset(expected, 0xcc, sizeof(expected));
|
||||
char buffer[LP_SECTOR_SIZE];
|
||||
ASSERT_GE(lseek(fd, exported->geometry.first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
|
||||
ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
|
||||
EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
|
||||
ASSERT_GE(lseek(fd, exported->geometry.last_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);
|
||||
ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));
|
||||
EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);
|
||||
}
|
||||
|
||||
// Test that we can read and write image files.
|
||||
TEST(liblp, ImageFiles) {
|
||||
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
|
||||
ASSERT_NE(builder, nullptr);
|
||||
ASSERT_TRUE(AddDefaultPartitions(builder.get()));
|
||||
unique_ptr<LpMetadata> exported = builder->Export();
|
||||
|
||||
unique_fd fd(syscall(__NR_memfd_create, "image_file", 0));
|
||||
ASSERT_GE(fd, 0);
|
||||
ASSERT_TRUE(WriteToImageFile(fd, *exported.get()));
|
||||
|
||||
unique_ptr<LpMetadata> imported = ReadFromImageFile(fd);
|
||||
ASSERT_NE(imported, nullptr);
|
||||
}
|
|
@ -76,7 +76,7 @@ static bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
|
|||
// Read and validate geometry information from a block device that holds
|
||||
// logical partitions. If the information is corrupted, this will attempt
|
||||
// to read it from a secondary backup location.
|
||||
static bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
|
||||
bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
|
||||
// Read the first 4096 bytes.
|
||||
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);
|
||||
if (SeekFile64(fd, 0, SEEK_SET) < 0) {
|
||||
|
@ -236,43 +236,51 @@ static std::unique_ptr<LpMetadata> ParseMetadata(int fd) {
|
|||
return metadata;
|
||||
}
|
||||
|
||||
std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
|
||||
android::base::unique_fd fd(open(block_device, O_RDONLY));
|
||||
if (fd < 0) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
|
||||
LpMetadataGeometry geometry;
|
||||
if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (slot_number >= geometry.metadata_slot_count) {
|
||||
LERROR << __PRETTY_FUNCTION__ << "invalid metadata slot number";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// First try the primary copy.
|
||||
int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);
|
||||
if (SeekFile64(fd, offset, SEEK_SET) < 0) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
|
||||
return nullptr;
|
||||
}
|
||||
if (std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd)) {
|
||||
return metadata;
|
||||
std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd);
|
||||
|
||||
// If the primary copy failed, try the backup copy.
|
||||
if (!metadata) {
|
||||
offset = GetBackupMetadataOffset(geometry, slot_number);
|
||||
if (SeekFile64(fd, offset, SEEK_END) < 0) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
|
||||
return nullptr;
|
||||
}
|
||||
metadata = ParseMetadata(fd);
|
||||
}
|
||||
|
||||
// Next try the backup copy.
|
||||
offset = GetBackupMetadataOffset(geometry, slot_number);
|
||||
if (SeekFile64(fd, offset, SEEK_END) < 0) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset;
|
||||
return nullptr;
|
||||
if (metadata) {
|
||||
metadata->geometry = geometry;
|
||||
}
|
||||
return ParseMetadata(fd);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
|
||||
android::base::unique_fd fd(open(file, O_RDONLY));
|
||||
std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number) {
|
||||
android::base::unique_fd fd(open(block_device, O_RDONLY));
|
||||
if (fd < 0) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
|
||||
PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
|
||||
return nullptr;
|
||||
}
|
||||
return ReadMetadata(fd, slot_number);
|
||||
}
|
||||
|
||||
std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {
|
||||
LpMetadataGeometry geometry;
|
||||
if (!ReadLogicalPartitionGeometry(fd, &geometry)) {
|
||||
return nullptr;
|
||||
|
@ -289,6 +297,15 @@ std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
|
|||
return metadata;
|
||||
}
|
||||
|
||||
std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
|
||||
android::base::unique_fd fd(open(file, O_RDONLY));
|
||||
if (fd < 0) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
|
||||
return nullptr;
|
||||
}
|
||||
return ReadFromImageFile(fd);
|
||||
}
|
||||
|
||||
static std::string NameFromFixedArray(const char* name, size_t buffer_size) {
|
||||
// If the end of the buffer has a null character, it's safe to assume the
|
||||
// buffer is null terminated. Otherwise, we cap the string to the input
|
||||
|
|
|
@ -124,14 +124,8 @@ static bool ValidateGeometryAndMetadata(const LpMetadata& metadata, uint64_t blo
|
|||
return true;
|
||||
}
|
||||
|
||||
bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
|
||||
bool WritePartitionTable(int fd, const LpMetadata& metadata, SyncMode sync_mode,
|
||||
uint32_t slot_number) {
|
||||
android::base::unique_fd fd(open(block_device, O_RDWR | O_SYNC));
|
||||
if (fd < 0) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t size;
|
||||
if (!GetDescriptorSize(fd, &size)) {
|
||||
return false;
|
||||
|
@ -142,7 +136,7 @@ bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, S
|
|||
// Verify that the old geometry is identical. If it's not, then we've
|
||||
// based this new metadata on invalid assumptions.
|
||||
LpMetadataGeometry old_geometry;
|
||||
if (!ReadLogicalPartitionGeometry(block_device, &old_geometry)) {
|
||||
if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
|
||||
return false;
|
||||
}
|
||||
if (!CompareGeometry(geometry, old_geometry)) {
|
||||
|
@ -174,8 +168,7 @@ bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, S
|
|||
return false;
|
||||
}
|
||||
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "write " << blob.size()
|
||||
<< " bytes failed: " << block_device;
|
||||
PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
|
||||
return false;
|
||||
}
|
||||
if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
|
||||
|
@ -183,8 +176,7 @@ bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, S
|
|||
return false;
|
||||
}
|
||||
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size()
|
||||
<< " bytes failed: " << block_device;
|
||||
PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -196,8 +188,7 @@ bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, S
|
|||
return false;
|
||||
}
|
||||
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "write " << blob.size()
|
||||
<< " bytes failed: " << block_device;
|
||||
PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -214,31 +205,44 @@ bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, S
|
|||
return false;
|
||||
}
|
||||
if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size()
|
||||
<< " bytes failed: " << block_device;
|
||||
PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteToImageFile(const char* file, const LpMetadata& input) {
|
||||
bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
|
||||
uint32_t slot_number) {
|
||||
android::base::unique_fd fd(open(block_device, O_RDWR | O_SYNC));
|
||||
if (fd < 0) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
|
||||
return false;
|
||||
}
|
||||
return WritePartitionTable(fd, metadata, sync_mode, slot_number);
|
||||
}
|
||||
|
||||
bool WriteToImageFile(int fd, const LpMetadata& input) {
|
||||
std::string geometry = SerializeGeometry(input.geometry);
|
||||
std::string padding(LP_METADATA_GEOMETRY_SIZE - geometry.size(), '\0');
|
||||
std::string metadata = SerializeMetadata(input);
|
||||
|
||||
std::string everything = geometry + padding + metadata;
|
||||
|
||||
android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
|
||||
if (fd < 0) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
|
||||
return false;
|
||||
}
|
||||
if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed: " << file;
|
||||
PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteToImageFile(const char* file, const LpMetadata& input) {
|
||||
android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
|
||||
if (fd < 0) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "open failed: " << file;
|
||||
return false;
|
||||
}
|
||||
return WriteToImageFile(fd, input);
|
||||
}
|
||||
|
||||
} // namespace fs_mgr
|
||||
} // namespace android
|
||||
|
|
Loading…
Reference in New Issue