diff options
Diffstat (limited to 'fs_mgr')
| -rw-r--r-- | fs_mgr/liblp/Android.bp | 2 | ||||
| -rw-r--r-- | fs_mgr/liblp/builder.cpp | 7 | ||||
| -rw-r--r-- | fs_mgr/liblp/builder_test.cpp | 21 | ||||
| -rw-r--r-- | fs_mgr/liblp/images.cpp | 192 | ||||
| -rw-r--r-- | fs_mgr/liblp/images.h | 29 | ||||
| -rw-r--r-- | fs_mgr/liblp/include/liblp/liblp.h | 1 | ||||
| -rw-r--r-- | fs_mgr/liblp/include/liblp/metadata_format.h | 9 | ||||
| -rw-r--r-- | fs_mgr/liblp/io_test.cpp | 1 | ||||
| -rw-r--r-- | fs_mgr/liblp/reader.cpp | 32 | ||||
| -rw-r--r-- | fs_mgr/liblp/reader.h | 3 | ||||
| -rw-r--r-- | fs_mgr/liblp/utility_test.cpp | 12 | ||||
| -rw-r--r-- | fs_mgr/liblp/writer.cpp | 32 | ||||
| -rw-r--r-- | fs_mgr/liblp/writer.h | 8 |
13 files changed, 268 insertions, 81 deletions
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp index 1050cf560..1434b212e 100644 --- a/fs_mgr/liblp/Android.bp +++ b/fs_mgr/liblp/Android.bp @@ -24,6 +24,7 @@ cc_library_static { ], srcs: [ "builder.cpp", + "images.cpp", "reader.cpp", "utility.cpp", "writer.cpp", @@ -33,6 +34,7 @@ cc_library_static { "liblog", "libcrypto", "libcrypto_utils", + "libsparse", ], whole_static_libs: [ "libext2_uuid", diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp index d15fa8c17..9d710f90b 100644 --- a/fs_mgr/liblp/builder.cpp +++ b/fs_mgr/liblp/builder.cpp @@ -200,6 +200,11 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE); // Check that device properties are sane. + device_info_ = device_info; + if (device_info_.size % LP_SECTOR_SIZE != 0) { + LERROR << "Block device size must be a multiple of 512."; + return false; + } if (device_info_.alignment_offset % LP_SECTOR_SIZE != 0) { LERROR << "Alignment offset is not sector-aligned."; return false; @@ -212,7 +217,6 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata LERROR << "Partition alignment offset is greater than its alignment."; return false; } - device_info_ = device_info; // We reserve a geometry block (4KB) plus space for each copy of the // maximum size of a metadata blob. Then, we double that space since @@ -250,6 +254,7 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata geometry_.metadata_slot_count = metadata_slot_count; geometry_.alignment = device_info_.alignment; geometry_.alignment_offset = device_info_.alignment_offset; + geometry_.block_device_size = device_info_.size; return true; } diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp index 08440a3d0..b610fd4c4 100644 --- a/fs_mgr/liblp/builder_test.cpp +++ b/fs_mgr/liblp/builder_test.cpp @@ -106,19 +106,9 @@ TEST(liblp, DiskAlignment) { static const uint32_t kMetadataSize = 1024; static const uint32_t kMetadataSlots = 2; - // If the disk size is not aligned to 512 bytes, make sure it still leaves - // space at the end for backup metadata, and that it doesn't overlap with - // the space for logical partitions. unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots); - unique_ptr<LpMetadata> exported = builder->Export(); - ASSERT_NE(exported, nullptr); - - static const size_t kMetadataSpace = - (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE; - uint64_t space_at_end = - kDiskSize - (exported->geometry.last_logical_sector + 1) * LP_SECTOR_SIZE; - EXPECT_GE(space_at_end, kMetadataSpace); + ASSERT_EQ(builder, nullptr); } TEST(liblp, MetadataAlignment) { @@ -148,15 +138,10 @@ TEST(liblp, InternalAlignment) { EXPECT_EQ(exported->geometry.first_logical_sector, 1472); EXPECT_EQ(exported->geometry.last_logical_sector, 2035); - // Test only an alignment offset (which should simply bump up the first - // logical sector). + // Alignment offset without alignment doesn't mean anything. device_info.alignment = 0; builder = MetadataBuilder::New(device_info, 1024, 2); - ASSERT_NE(builder, nullptr); - exported = builder->Export(); - ASSERT_NE(exported, nullptr); - EXPECT_EQ(exported->geometry.first_logical_sector, 1484); - EXPECT_EQ(exported->geometry.last_logical_sector, 2035); + ASSERT_EQ(builder, nullptr); // Test a small alignment with an alignment offset. device_info.alignment = 12 * 1024; diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp new file mode 100644 index 000000000..93c56180f --- /dev/null +++ b/fs_mgr/liblp/images.cpp @@ -0,0 +1,192 @@ +/* + * 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 "images.h" + +#include <limits.h> + +#include <android-base/file.h> +#include <android-base/unique_fd.h> +#include <sparse/sparse.h> + +#include "reader.h" +#include "utility.h" +#include "writer.h" + +namespace android { +namespace fs_mgr { + +std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) { + LpMetadataGeometry geometry; + if (!ReadLogicalPartitionGeometry(fd, &geometry)) { + return nullptr; + } + if (SeekFile64(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET) < 0) { + PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE; + return nullptr; + } + std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd); + if (!metadata) { + return nullptr; + } + metadata->geometry = geometry; + 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); +} + +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; + + if (!android::base::WriteFully(fd, everything.data(), everything.size())) { + 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); +} + +// We use an object to build the sparse file since it requires that data +// pointers be held alive until the sparse file is destroyed. It's easier +// to do this when the data pointers are all in one place. +class SparseBuilder { + public: + explicit SparseBuilder(const LpMetadata& metadata); + + bool Build(); + bool Export(const char* file); + bool IsValid() const { return file_ != nullptr; } + + private: + bool AddData(const std::string& blob, uint32_t block); + + const LpMetadata& metadata_; + const LpMetadataGeometry& geometry_; + std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_; + std::string geometry_blob_; + std::string metadata_blob_; +}; + +SparseBuilder::SparseBuilder(const LpMetadata& metadata) + : metadata_(metadata), + geometry_(metadata.geometry), + file_(sparse_file_new(LP_SECTOR_SIZE, geometry_.block_device_size), sparse_file_destroy) {} + +bool SparseBuilder::Export(const char* file) { + android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644)); + if (fd < 0) { + PERROR << "open failed: " << file; + return false; + } + // No gzip compression; sparseify; no checksum. + int ret = sparse_file_write(file_.get(), fd, false, true, false); + if (ret != 0) { + LERROR << "sparse_file_write failed (error code " << ret << ")"; + return false; + } + return true; +} + +bool SparseBuilder::AddData(const std::string& blob, uint32_t block) { + void* data = const_cast<char*>(blob.data()); + int ret = sparse_file_add_data(file_.get(), data, blob.size(), block); + if (ret != 0) { + LERROR << "sparse_file_add_data failed (error code " << ret << ")"; + return false; + } + return true; +} + +bool SparseBuilder::Build() { + geometry_blob_ = SerializeGeometry(geometry_); + geometry_blob_.resize(LP_METADATA_GEOMETRY_SIZE); + if (!AddData(geometry_blob_, 0)) { + return false; + } + + // Metadata immediately follows geometry, and we write the same metadata + // to all slots. + uint32_t metadata_block = LP_METADATA_GEOMETRY_SIZE / LP_SECTOR_SIZE; + metadata_blob_ = SerializeMetadata(metadata_); + for (size_t i = 0; i < geometry_.metadata_slot_count; i++) { + if (!AddData(metadata_blob_, metadata_block)) { + return false; + } + metadata_block += geometry_.metadata_max_size / LP_SECTOR_SIZE; + } + + // The backup area contains all metadata slots, and then geometry. Similar + // to before we write the metadata to every slot. + int64_t backup_offset = GetBackupMetadataOffset(geometry_, 0); + uint64_t backups_start = geometry_.block_device_size + backup_offset; + uint64_t backup_sector = backups_start / LP_SECTOR_SIZE; + for (size_t i = 0; i < geometry_.metadata_slot_count; i++) { + if (!AddData(metadata_blob_, backup_sector)) { + return false; + } + backup_sector += geometry_.metadata_max_size / LP_SECTOR_SIZE; + } + if (!AddData(geometry_blob_, backup_sector)) { + return false; + } + return true; +} + +bool WriteToSparseFile(const char* file, const LpMetadata& metadata) { + uint64_t num_blocks = + AlignTo(metadata.geometry.block_device_size, LP_SECTOR_SIZE) / LP_SECTOR_SIZE; + if (num_blocks >= UINT_MAX) { + // libsparse counts blocks in unsigned 32-bit integers, but our block + // size is rather low (512 bytes), since we operate in sectors. + // Therefore the maximum block device size we can represent with a + // sparse file is 2TB for now. + LERROR << "Block device is too large to encode with libsparse."; + return false; + } + + SparseBuilder builder(metadata); + if (!builder.IsValid()) { + LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size; + return false; + } + if (!builder.Build()) { + return false; + } + + return builder.Export(file); +} + +} // namespace fs_mgr +} // namespace android diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h new file mode 100644 index 000000000..3a999b89c --- /dev/null +++ b/fs_mgr/liblp/images.h @@ -0,0 +1,29 @@ +/* + * 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 <liblp/liblp.h> + +namespace android { +namespace fs_mgr { + +// Helper function to serialize geometry and metadata to a normal file, for +// flashing or debugging. +std::unique_ptr<LpMetadata> ReadFromImageFile(int fd); +bool WriteToImageFile(const char* file, const LpMetadata& metadata); +bool WriteToImageFile(int fd, const LpMetadata& metadata); + +} // namespace fs_mgr +} // namespace android diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h index 469ef9e59..c8d34d969 100644 --- a/fs_mgr/liblp/include/liblp/liblp.h +++ b/fs_mgr/liblp/include/liblp/liblp.h @@ -59,6 +59,7 @@ std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot // Read/Write logical partition metadata to an image file, for diagnostics or // flashing. +bool WriteToSparseFile(const char* file, const LpMetadata& metadata); bool WriteToImageFile(const char* file, const LpMetadata& metadata); std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file); diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h index b5202f030..e1323e11e 100644 --- a/fs_mgr/liblp/include/liblp/metadata_format.h +++ b/fs_mgr/liblp/include/liblp/metadata_format.h @@ -86,7 +86,9 @@ typedef struct LpMetadataGeometry { /* 8: SHA256 checksum of this struct, with this field set to 0. */ uint8_t checksum[32]; - /* 40: Maximum amount of space a single copy of the metadata can use. */ + /* 40: Maximum amount of space a single copy of the metadata can use. This + * must be a multiple of LP_SECTOR_SIZE. + */ uint32_t metadata_max_size; /* 44: Number of copies of the metadata to keep. For A/B devices, this @@ -129,6 +131,11 @@ typedef struct LpMetadataGeometry { * If it cannot be determined, it is assumed to be 0. */ uint32_t alignment_offset; + + /* 72: Block device size, as specified when the metadata was created. This + * can be used to verify the geometry against a target device. + */ + uint64_t block_device_size; } __attribute__((packed)) LpMetadataGeometry; /* The logical partition metadata has a number of tables; they are described diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp index e91cc3e0a..bbbedc761 100644 --- a/fs_mgr/liblp/io_test.cpp +++ b/fs_mgr/liblp/io_test.cpp @@ -24,6 +24,7 @@ #include <gtest/gtest.h> #include <liblp/builder.h> +#include "images.h" #include "reader.h" #include "utility.h" #include "writer.h" diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp index 985cf0991..117da5991 100644 --- a/fs_mgr/liblp/reader.cpp +++ b/fs_mgr/liblp/reader.cpp @@ -68,6 +68,10 @@ static bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) { LERROR << "Logical partition metadata has invalid slot count."; return false; } + if (geometry->metadata_max_size % LP_SECTOR_SIZE != 0) { + LERROR << "Metadata max size is not sector-aligned."; + return false; + } // Check that the metadata area and logical partition areas don't overlap. int64_t end_of_metadata = @@ -167,7 +171,7 @@ static bool ValidateMetadataHeader(const LpMetadataHeader& header) { // Parse and validate all metadata at the current position in the given file // descriptor. -static std::unique_ptr<LpMetadata> ParseMetadata(int fd) { +std::unique_ptr<LpMetadata> ParseMetadata(int fd) { // First read and validate the header. std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>(); if (!android::base::ReadFully(fd, &metadata->header, sizeof(metadata->header))) { @@ -282,32 +286,6 @@ std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot return ReadMetadata(fd, slot_number); } -std::unique_ptr<LpMetadata> ReadFromImageFile(int fd) { - LpMetadataGeometry geometry; - if (!ReadLogicalPartitionGeometry(fd, &geometry)) { - return nullptr; - } - if (SeekFile64(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET) < 0) { - PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << LP_METADATA_GEOMETRY_SIZE; - return nullptr; - } - std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd); - if (!metadata) { - return nullptr; - } - metadata->geometry = geometry; - 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 diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h index c4cac8fcb..843b2f25b 100644 --- a/fs_mgr/liblp/reader.h +++ b/fs_mgr/liblp/reader.h @@ -30,6 +30,7 @@ std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number); // Helper functions for manually reading geometry and metadata. bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry); +std::unique_ptr<LpMetadata> ParseMetadata(int fd); // These functions assume a valid geometry and slot number. std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry, @@ -37,8 +38,6 @@ std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number); -std::unique_ptr<LpMetadata> ReadFromImageFile(int fd); - } // namespace fs_mgr } // namespace android diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp index 2143e13de..092dbf11c 100644 --- a/fs_mgr/liblp/utility_test.cpp +++ b/fs_mgr/liblp/utility_test.cpp @@ -31,8 +31,16 @@ TEST(liblp, SlotNumberForSlotSuffix) { } TEST(liblp, GetMetadataOffset) { - LpMetadataGeometry geometry = { - LP_METADATA_GEOMETRY_MAGIC, sizeof(geometry), {0}, 16384, 4, 10000, 80000, 0, 0}; + LpMetadataGeometry geometry = {LP_METADATA_GEOMETRY_MAGIC, + sizeof(geometry), + {0}, + 16384, + 4, + 10000, + 80000, + 0, + 0, + 1024 * 1024}; EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096); EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384); EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2); diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp index b85e4ad6c..74c03bf2f 100644 --- a/fs_mgr/liblp/writer.cpp +++ b/fs_mgr/liblp/writer.cpp @@ -30,7 +30,7 @@ namespace android { namespace fs_mgr { -static std::string SerializeGeometry(const LpMetadataGeometry& input) { +std::string SerializeGeometry(const LpMetadataGeometry& input) { LpMetadataGeometry geometry = input; memset(geometry.checksum, 0, sizeof(geometry.checksum)); SHA256(&geometry, sizeof(geometry), geometry.checksum); @@ -44,7 +44,7 @@ static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeomet g1.last_logical_sector == g2.last_logical_sector; } -static std::string SerializeMetadata(const LpMetadata& input) { +std::string SerializeMetadata(const LpMetadata& input) { LpMetadata metadata = input; LpMetadataHeader& header = metadata.header; @@ -108,6 +108,11 @@ static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std LERROR << "Not enough space to backup all logical partition metadata slots."; return false; } + if (blockdevice_size != metadata.geometry.block_device_size) { + LERROR << "Block device size " << blockdevice_size + << " does not match metadata requested size " << metadata.geometry.block_device_size; + return false; + } // Make sure all partition entries reference valid extents. for (const auto& partition : metadata.partitions) { @@ -313,28 +318,5 @@ bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_numb return UpdatePartitionTable(fd, metadata, slot_number, DefaultWriter); } -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; - - if (!android::base::WriteFully(fd, everything.data(), everything.size())) { - 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 diff --git a/fs_mgr/liblp/writer.h b/fs_mgr/liblp/writer.h index 94c1d3195..adbbebf66 100644 --- a/fs_mgr/liblp/writer.h +++ b/fs_mgr/liblp/writer.h @@ -25,6 +25,9 @@ namespace android { namespace fs_mgr { +std::string SerializeGeometry(const LpMetadataGeometry& input); +std::string SerializeMetadata(const LpMetadata& input); + // These variants are for testing only. The path-based functions should be used // for actual operation, so that open() is called with the correct flags. bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number); @@ -33,11 +36,6 @@ bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_numb bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number, const std::function<bool(int, const std::string&)>& writer); -// 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 |
