From b36db4ba0ed8188423c8a139c362a5dc59b57e9a Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 13 Jul 2018 17:22:12 -0700 Subject: liblp: Add a method to create sparse images of the super partition. This change enables flashing of the super partition without using lpflash or adding explicit fastboot support for the liblp image format. Bug: 79173901 Test: image can be flashed to device and read by liblp Change-Id: Id6c5e595ba831644364b7335b58cf6a43d5833c3 --- fs_mgr/liblp/Android.bp | 1 + fs_mgr/liblp/images.cpp | 113 +++++++++++++++++++++++++++++++++++++ fs_mgr/liblp/include/liblp/liblp.h | 1 + 3 files changed, 115 insertions(+) (limited to 'fs_mgr') diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp index 176671a59..1434b212e 100644 --- a/fs_mgr/liblp/Android.bp +++ b/fs_mgr/liblp/Android.bp @@ -34,6 +34,7 @@ cc_library_static { "liblog", "libcrypto", "libcrypto_utils", + "libsparse", ], whole_static_libs: [ "libext2_uuid", diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp index c8d9f13d8..93c56180f 100644 --- a/fs_mgr/liblp/images.cpp +++ b/fs_mgr/liblp/images.cpp @@ -16,8 +16,11 @@ #include "images.h" +#include + #include #include +#include #include "reader.h" #include "utility.h" @@ -75,5 +78,115 @@ bool WriteToImageFile(const char* file, const LpMetadata& input) { 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 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(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/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 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 ReadFromImageFile(const char* file); -- cgit v1.2.3