Merge "liblp: Allow specifying a custom block size when building sparse images."

This commit is contained in:
Treehugger Robot 2018-07-18 21:08:16 +00:00 committed by Gerrit Code Review
commit 54f40303d5
3 changed files with 63 additions and 38 deletions

View File

@ -57,10 +57,9 @@ std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
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;
std::string everything = geometry + metadata;
if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed";
@ -83,26 +82,29 @@ bool WriteToImageFile(const char* file, const LpMetadata& input) {
// to do this when the data pointers are all in one place.
class SparseBuilder {
public:
explicit SparseBuilder(const LpMetadata& metadata);
SparseBuilder(const LpMetadata& metadata, uint32_t block_size);
bool Build();
bool Export(const char* file);
bool IsValid() const { return file_ != nullptr; }
private:
bool AddData(const std::string& blob, uint32_t block);
bool AddData(const std::string& blob, uint64_t sector);
bool SectorToBlock(uint64_t sector, uint32_t* block);
const LpMetadata& metadata_;
const LpMetadataGeometry& geometry_;
uint32_t block_size_;
std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
std::string geometry_blob_;
std::string metadata_blob_;
std::string primary_blob_;
std::string backup_blob_;
};
SparseBuilder::SparseBuilder(const LpMetadata& metadata)
SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size)
: metadata_(metadata),
geometry_(metadata.geometry),
file_(sparse_file_new(LP_SECTOR_SIZE, geometry_.block_device_size), sparse_file_destroy) {}
block_size_(block_size),
file_(sparse_file_new(block_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));
@ -119,7 +121,11 @@ bool SparseBuilder::Export(const char* file) {
return true;
}
bool SparseBuilder::AddData(const std::string& blob, uint32_t block) {
bool SparseBuilder::AddData(const std::string& blob, uint64_t sector) {
uint32_t block;
if (!SectorToBlock(sector, &block)) {
return false;
}
void* data = const_cast<char*>(blob.data());
int ret = sparse_file_add_data(file_.get(), data, blob.size(), block);
if (ret != 0) {
@ -129,22 +135,37 @@ bool SparseBuilder::AddData(const std::string& blob, uint32_t block) {
return true;
}
bool SparseBuilder::Build() {
geometry_blob_ = SerializeGeometry(geometry_);
geometry_blob_.resize(LP_METADATA_GEOMETRY_SIZE);
if (!AddData(geometry_blob_, 0)) {
bool SparseBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
// The caller must ensure that the metadata has an alignment that is a
// multiple of the block size. liblp will take care of the rest, ensuring
// that all partitions are on an aligned boundary. Therefore all writes
// should be block-aligned, and if they are not, the table was misconfigured.
// Note that the default alignment is 1MiB, which is a multiple of the
// default block size (4096).
if ((sector * LP_SECTOR_SIZE) % block_size_ != 0) {
LERROR << "sector " << sector << " is not aligned to block size " << block_size_;
return false;
}
*block = (sector * LP_SECTOR_SIZE) / block_size_;
return true;
}
bool SparseBuilder::Build() {
std::string geometry_blob = SerializeGeometry(geometry_);
std::string metadata_blob = SerializeMetadata(metadata_);
metadata_blob.resize(geometry_.metadata_max_size);
std::string all_metadata;
for (size_t i = 0; i < geometry_.metadata_slot_count; i++) {
all_metadata += metadata_blob;
}
// 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;
// to all slots. Note that we don't bother trying to write skip chunks
// here since it's a small amount of data.
primary_blob_ = geometry_blob + all_metadata;
if (!AddData(primary_blob_, 0)) {
return false;
}
// The backup area contains all metadata slots, and then geometry. Similar
@ -152,31 +173,32 @@ bool SparseBuilder::Build() {
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)) {
backup_blob_ = all_metadata + geometry_blob;
if (!AddData(backup_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;
bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size) {
if (block_size % LP_SECTOR_SIZE != 0) {
LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
return false;
}
if (metadata.geometry.block_device_size % block_size != 0) {
LERROR << "Device size must be a multiple of the block size, " << block_size;
return false;
}
uint64_t num_blocks = metadata.geometry.block_device_size % block_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.
// libsparse counts blocks in unsigned 32-bit integers, so we check to
// make sure we're not going to overflow.
LERROR << "Block device is too large to encode with libsparse.";
return false;
}
SparseBuilder builder(metadata);
SparseBuilder builder(metadata, block_size);
if (!builder.IsValid()) {
LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size;
return false;

View File

@ -59,7 +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 WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size);
bool WriteToImageFile(const char* file, const LpMetadata& metadata);
std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);

View File

@ -34,7 +34,10 @@ std::string SerializeGeometry(const LpMetadataGeometry& input) {
LpMetadataGeometry geometry = input;
memset(geometry.checksum, 0, sizeof(geometry.checksum));
SHA256(&geometry, sizeof(geometry), geometry.checksum);
return std::string(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
std::string blob(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
blob.resize(LP_METADATA_GEOMETRY_SIZE);
return blob;
}
static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {