diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp index aeb339b01..a361a5d93 100644 --- a/fs_mgr/liblp/images.cpp +++ b/fs_mgr/liblp/images.cpp @@ -82,7 +82,8 @@ bool WriteToImageFile(const char* file, const LpMetadata& input) { // to do this when the data pointers are all in one place. class SparseBuilder { public: - SparseBuilder(const LpMetadata& metadata, uint32_t block_size); + SparseBuilder(const LpMetadata& metadata, uint32_t block_size, + const std::map& images); bool Build(); bool Export(const char* file); @@ -90,6 +91,8 @@ class SparseBuilder { private: bool AddData(const std::string& blob, uint64_t sector); + bool AddPartitionImage(const LpMetadataPartition& partition, const std::string& file); + int OpenImageFile(const std::string& file); bool SectorToBlock(uint64_t sector, uint32_t* block); const LpMetadata& metadata_; @@ -98,13 +101,17 @@ class SparseBuilder { std::unique_ptr file_; std::string primary_blob_; std::string backup_blob_; + std::map images_; + std::vector temp_fds_; }; -SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size) +SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size, + const std::map& images) : metadata_(metadata), geometry_(metadata.geometry), block_size_(block_size), - file_(sparse_file_new(block_size_, geometry_.block_device_size), sparse_file_destroy) {} + file_(sparse_file_new(block_size_, geometry_.block_device_size), sparse_file_destroy), + images_(images) {} bool SparseBuilder::Export(const char* file) { android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644)); @@ -168,6 +175,22 @@ bool SparseBuilder::Build() { return false; } + for (const auto& partition : metadata_.partitions) { + auto iter = images_.find(GetPartitionName(partition)); + if (iter == images_.end()) { + continue; + } + if (!AddPartitionImage(partition, iter->second)) { + return false; + } + images_.erase(iter); + } + + if (!images_.empty()) { + LERROR << "Partition image was specified but no partition was found."; + return false; + } + // 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); @@ -181,7 +204,126 @@ bool SparseBuilder::Build() { return true; } -bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size) { +static inline bool HasFillValue(uint32_t* buffer, size_t count) { + uint32_t fill_value = buffer[0]; + for (size_t i = 1; i < count; i++) { + if (fill_value != buffer[i]) { + return false; + } + } + return true; +} + +bool SparseBuilder::AddPartitionImage(const LpMetadataPartition& partition, + const std::string& file) { + if (partition.num_extents != 1) { + LERROR << "Partition for new tables should not have more than one extent: " + << GetPartitionName(partition); + return false; + } + + const LpMetadataExtent& extent = metadata_.extents[partition.first_extent_index]; + if (extent.target_type != LP_TARGET_TYPE_LINEAR) { + LERROR << "Partition should only have linear extents: " << GetPartitionName(partition); + return false; + } + + int fd = OpenImageFile(file); + if (fd < 0) { + LERROR << "Could not open image for partition: " << GetPartitionName(partition); + return false; + } + + // Make sure the image does not exceed the partition size. + uint64_t file_length; + if (!GetDescriptorSize(fd, &file_length)) { + LERROR << "Could not compute image size"; + return false; + } + if (file_length > extent.num_sectors * LP_SECTOR_SIZE) { + LERROR << "Image for partition '" << GetPartitionName(partition) + << "' is greater than its size"; + return false; + } + if (SeekFile64(fd, 0, SEEK_SET)) { + PERROR << "lseek failed"; + return false; + } + + uint32_t output_block; + if (!SectorToBlock(extent.target_data, &output_block)) { + return false; + } + + uint64_t pos = 0; + uint64_t remaining = file_length; + while (remaining) { + uint32_t buffer[block_size_ / sizeof(uint32_t)]; + size_t read_size = remaining >= sizeof(buffer) ? sizeof(buffer) : size_t(remaining); + if (!android::base::ReadFully(fd, buffer, sizeof(buffer))) { + PERROR << "read failed"; + return false; + } + if (read_size != sizeof(buffer) || !HasFillValue(buffer, read_size / sizeof(uint32_t))) { + int rv = sparse_file_add_fd(file_.get(), fd, pos, read_size, output_block); + if (rv) { + LERROR << "sparse_file_add_fd failed with code: " << rv; + return false; + } + } else { + int rv = sparse_file_add_fill(file_.get(), buffer[0], read_size, output_block); + if (rv) { + LERROR << "sparse_file_add_fill failed with code: " << rv; + return false; + } + } + pos += read_size; + remaining -= read_size; + output_block++; + } + + return true; +} + +int SparseBuilder::OpenImageFile(const std::string& file) { + android::base::unique_fd source_fd(open(file.c_str(), O_RDONLY)); + if (source_fd < 0) { + PERROR << "open image file failed: " << file; + return -1; + } + + std::unique_ptr source( + sparse_file_import(source_fd, true, true), sparse_file_destroy); + if (!source) { + int fd = source_fd.get(); + temp_fds_.push_back(std::move(source_fd)); + return fd; + } + + char temp_file[PATH_MAX]; + snprintf(temp_file, sizeof(temp_file), "%s/imageXXXXXX", P_tmpdir); + android::base::unique_fd temp_fd(mkstemp(temp_file)); + if (temp_fd < 0) { + PERROR << "mkstemp failed"; + return -1; + } + if (unlink(temp_file) < 0) { + PERROR << "unlink failed"; + return -1; + } + + // We temporarily unsparse the file, rather than try to merge its chunks. + int rv = sparse_file_write(source.get(), temp_fd, false, false, false); + if (rv) { + LERROR << "sparse_file_write failed with code: " << rv; + return -1; + } + temp_fds_.push_back(std::move(temp_fd)); + return temp_fds_.back().get(); +} + +bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size, + const std::map& images) { if (block_size % LP_SECTOR_SIZE != 0) { LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE; return false; @@ -198,7 +340,7 @@ bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t bl return false; } - SparseBuilder builder(metadata, block_size); + SparseBuilder builder(metadata, block_size, images); if (!builder.IsValid()) { LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size; return false; @@ -206,7 +348,6 @@ bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t bl if (!builder.Build()) { return false; } - return builder.Export(file); } diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h index a9f01c6c8..627aa8cad 100644 --- a/fs_mgr/liblp/include/liblp/liblp.h +++ b/fs_mgr/liblp/include/liblp/liblp.h @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -59,7 +60,8 @@ 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, uint32_t block_size); +bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size, + const std::map& images); bool WriteToImageFile(const char* file, const LpMetadata& metadata); std::unique_ptr ReadFromImageFile(const char* file);