Loading fs_mgr/liblp/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ cc_test { "liblp", "libbase", "libfs_mgr", "libsparse", ], srcs: [ "builder_test.cpp", Loading fs_mgr/liblp/images.cpp +27 −50 Original line number Diff line number Diff line Loading @@ -19,8 +19,6 @@ #include <limits.h> #include <android-base/file.h> #include <android-base/unique_fd.h> #include <sparse/sparse.h> #include "reader.h" #include "utility.h" Loading Loading @@ -89,41 +87,36 @@ 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: SparseBuilder(const LpMetadata& metadata, uint32_t block_size, const std::map<std::string, std::string>& images); bool Build(); bool Export(const char* file); bool IsValid() const { return file_ != nullptr; } 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_; const LpMetadataGeometry& geometry_; uint32_t block_size_; std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_; std::string primary_blob_; std::string backup_blob_; std::map<std::string, std::string> images_; std::vector<android::base::unique_fd> temp_fds_; }; SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size, const std::map<std::string, std::string>& images) : metadata_(metadata), geometry_(metadata.geometry), block_size_(block_size), file_(sparse_file_new(block_size_, geometry_.block_device_size), sparse_file_destroy), images_(images) {} file_(nullptr, sparse_file_destroy), images_(images) { if (block_size % LP_SECTOR_SIZE != 0) { LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE; return; } if (metadata.geometry.block_device_size % block_size != 0) { LERROR << "Device size must be a multiple of the block size, " << block_size; return; } if (metadata.geometry.metadata_max_size % block_size != 0) { LERROR << "Metadata max size must be a multiple of the block size, " << block_size; return; } uint64_t num_blocks = metadata.geometry.block_device_size % block_size; if (num_blocks >= UINT_MAX) { // 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; } file_.reset(sparse_file_new(block_size_, geometry_.block_device_size)); } bool SparseBuilder::Export(const char* file) { android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644)); Loading Loading @@ -206,8 +199,8 @@ bool SparseBuilder::Build() { // 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; int64_t backups_start = static_cast<int64_t>(geometry_.block_device_size) + backup_offset; int64_t backup_sector = backups_start / LP_SECTOR_SIZE; backup_blob_ = all_metadata + geometry_blob; if (!AddData(backup_blob_, backup_sector)) { Loading Loading @@ -336,22 +329,6 @@ int SparseBuilder::OpenImageFile(const std::string& file) { bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size, const std::map<std::string, std::string>& images) { 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, 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, block_size, images); if (!builder.IsValid()) { LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size; Loading fs_mgr/liblp/images.h +37 −0 Original line number Diff line number Diff line Loading @@ -14,7 +14,14 @@ * limitations under the License. */ #include <stdint.h> #include <map> #include <memory> #include <string> #include <android-base/unique_fd.h> #include <liblp/liblp.h> #include <sparse/sparse.h> namespace android { namespace fs_mgr { Loading @@ -25,5 +32,35 @@ std::unique_ptr<LpMetadata> ReadFromImageFile(int fd); bool WriteToImageFile(const char* file, const LpMetadata& metadata); bool WriteToImageFile(int fd, const LpMetadata& metadata); // 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: SparseBuilder(const LpMetadata& metadata, uint32_t block_size, const std::map<std::string, std::string>& images); bool Build(); bool Export(const char* file); bool IsValid() const { return file_ != nullptr; } sparse_file* file() const { return file_.get(); } 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_; const LpMetadataGeometry& geometry_; uint32_t block_size_; std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_; std::string primary_blob_; std::string backup_blob_; std::map<std::string, std::string> images_; std::vector<android::base::unique_fd> temp_fds_; }; } // namespace fs_mgr } // namespace android fs_mgr/liblp/io_test.cpp +33 −0 Original line number Diff line number Diff line Loading @@ -535,3 +535,36 @@ TEST(liblp, UpdateMetadataCleanFailure) { ASSERT_GE(new_table->partitions.size(), 1); ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0])); } // Test that writing a sparse image can be read back. TEST(liblp, FlashSparseImage) { unique_fd fd = CreateFakeDisk(); ASSERT_GE(fd, 0); BlockDeviceInfo device_info(kDiskSize, 0, 0, 512); unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, kMetadataSlots); ASSERT_NE(builder, nullptr); ASSERT_TRUE(AddDefaultPartitions(builder.get())); unique_ptr<LpMetadata> exported = builder->Export(); ASSERT_NE(exported, nullptr); // Build the sparse file. SparseBuilder sparse(*exported.get(), 512, {}); ASSERT_TRUE(sparse.IsValid()); sparse_file_verbose(sparse.file()); ASSERT_TRUE(sparse.Build()); // Write it to the fake disk. ASSERT_NE(lseek(fd.get(), 0, SEEK_SET), -1); int ret = sparse_file_write(sparse.file(), fd.get(), false, false, false); ASSERT_EQ(ret, 0); // Verify that we can read both sets of metadata. LpMetadataGeometry geometry; ASSERT_TRUE(ReadPrimaryGeometry(fd.get(), &geometry)); ASSERT_TRUE(ReadBackupGeometry(fd.get(), &geometry)); ASSERT_NE(ReadPrimaryMetadata(fd.get(), geometry, 0), nullptr); ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr); } fs_mgr/liblp/reader.cpp +15 −7 Original line number Diff line number Diff line Loading @@ -120,10 +120,7 @@ bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) { return true; } // 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. bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) { bool ReadPrimaryGeometry(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) { Loading @@ -134,11 +131,12 @@ bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) { PERROR << __PRETTY_FUNCTION__ << "read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed"; return false; } if (ParseGeometry(buffer.get(), geometry)) { return true; return ParseGeometry(buffer.get(), geometry); } bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry) { // Try the backup copy in the last 4096 bytes. std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE); if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE; return false; Loading @@ -151,6 +149,16 @@ bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) { return ParseGeometry(buffer.get(), 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. bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) { if (ReadPrimaryGeometry(fd, geometry)) { return true; } return ReadBackupGeometry(fd, geometry); } static bool ValidateTableBounds(const LpMetadataHeader& header, const LpMetadataTableDescriptor& table) { if (table.offset > header.tables_size) { Loading Loading
fs_mgr/liblp/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ cc_test { "liblp", "libbase", "libfs_mgr", "libsparse", ], srcs: [ "builder_test.cpp", Loading
fs_mgr/liblp/images.cpp +27 −50 Original line number Diff line number Diff line Loading @@ -19,8 +19,6 @@ #include <limits.h> #include <android-base/file.h> #include <android-base/unique_fd.h> #include <sparse/sparse.h> #include "reader.h" #include "utility.h" Loading Loading @@ -89,41 +87,36 @@ 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: SparseBuilder(const LpMetadata& metadata, uint32_t block_size, const std::map<std::string, std::string>& images); bool Build(); bool Export(const char* file); bool IsValid() const { return file_ != nullptr; } 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_; const LpMetadataGeometry& geometry_; uint32_t block_size_; std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_; std::string primary_blob_; std::string backup_blob_; std::map<std::string, std::string> images_; std::vector<android::base::unique_fd> temp_fds_; }; SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size, const std::map<std::string, std::string>& images) : metadata_(metadata), geometry_(metadata.geometry), block_size_(block_size), file_(sparse_file_new(block_size_, geometry_.block_device_size), sparse_file_destroy), images_(images) {} file_(nullptr, sparse_file_destroy), images_(images) { if (block_size % LP_SECTOR_SIZE != 0) { LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE; return; } if (metadata.geometry.block_device_size % block_size != 0) { LERROR << "Device size must be a multiple of the block size, " << block_size; return; } if (metadata.geometry.metadata_max_size % block_size != 0) { LERROR << "Metadata max size must be a multiple of the block size, " << block_size; return; } uint64_t num_blocks = metadata.geometry.block_device_size % block_size; if (num_blocks >= UINT_MAX) { // 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; } file_.reset(sparse_file_new(block_size_, geometry_.block_device_size)); } bool SparseBuilder::Export(const char* file) { android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644)); Loading Loading @@ -206,8 +199,8 @@ bool SparseBuilder::Build() { // 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; int64_t backups_start = static_cast<int64_t>(geometry_.block_device_size) + backup_offset; int64_t backup_sector = backups_start / LP_SECTOR_SIZE; backup_blob_ = all_metadata + geometry_blob; if (!AddData(backup_blob_, backup_sector)) { Loading Loading @@ -336,22 +329,6 @@ int SparseBuilder::OpenImageFile(const std::string& file) { bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size, const std::map<std::string, std::string>& images) { 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, 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, block_size, images); if (!builder.IsValid()) { LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size; Loading
fs_mgr/liblp/images.h +37 −0 Original line number Diff line number Diff line Loading @@ -14,7 +14,14 @@ * limitations under the License. */ #include <stdint.h> #include <map> #include <memory> #include <string> #include <android-base/unique_fd.h> #include <liblp/liblp.h> #include <sparse/sparse.h> namespace android { namespace fs_mgr { Loading @@ -25,5 +32,35 @@ std::unique_ptr<LpMetadata> ReadFromImageFile(int fd); bool WriteToImageFile(const char* file, const LpMetadata& metadata); bool WriteToImageFile(int fd, const LpMetadata& metadata); // 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: SparseBuilder(const LpMetadata& metadata, uint32_t block_size, const std::map<std::string, std::string>& images); bool Build(); bool Export(const char* file); bool IsValid() const { return file_ != nullptr; } sparse_file* file() const { return file_.get(); } 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_; const LpMetadataGeometry& geometry_; uint32_t block_size_; std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_; std::string primary_blob_; std::string backup_blob_; std::map<std::string, std::string> images_; std::vector<android::base::unique_fd> temp_fds_; }; } // namespace fs_mgr } // namespace android
fs_mgr/liblp/io_test.cpp +33 −0 Original line number Diff line number Diff line Loading @@ -535,3 +535,36 @@ TEST(liblp, UpdateMetadataCleanFailure) { ASSERT_GE(new_table->partitions.size(), 1); ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0])); } // Test that writing a sparse image can be read back. TEST(liblp, FlashSparseImage) { unique_fd fd = CreateFakeDisk(); ASSERT_GE(fd, 0); BlockDeviceInfo device_info(kDiskSize, 0, 0, 512); unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, kMetadataSlots); ASSERT_NE(builder, nullptr); ASSERT_TRUE(AddDefaultPartitions(builder.get())); unique_ptr<LpMetadata> exported = builder->Export(); ASSERT_NE(exported, nullptr); // Build the sparse file. SparseBuilder sparse(*exported.get(), 512, {}); ASSERT_TRUE(sparse.IsValid()); sparse_file_verbose(sparse.file()); ASSERT_TRUE(sparse.Build()); // Write it to the fake disk. ASSERT_NE(lseek(fd.get(), 0, SEEK_SET), -1); int ret = sparse_file_write(sparse.file(), fd.get(), false, false, false); ASSERT_EQ(ret, 0); // Verify that we can read both sets of metadata. LpMetadataGeometry geometry; ASSERT_TRUE(ReadPrimaryGeometry(fd.get(), &geometry)); ASSERT_TRUE(ReadBackupGeometry(fd.get(), &geometry)); ASSERT_NE(ReadPrimaryMetadata(fd.get(), geometry, 0), nullptr); ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr); }
fs_mgr/liblp/reader.cpp +15 −7 Original line number Diff line number Diff line Loading @@ -120,10 +120,7 @@ bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) { return true; } // 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. bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) { bool ReadPrimaryGeometry(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) { Loading @@ -134,11 +131,12 @@ bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) { PERROR << __PRETTY_FUNCTION__ << "read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed"; return false; } if (ParseGeometry(buffer.get(), geometry)) { return true; return ParseGeometry(buffer.get(), geometry); } bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry) { // Try the backup copy in the last 4096 bytes. std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE); if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE; return false; Loading @@ -151,6 +149,16 @@ bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) { return ParseGeometry(buffer.get(), 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. bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) { if (ReadPrimaryGeometry(fd, geometry)) { return true; } return ReadBackupGeometry(fd, geometry); } static bool ValidateTableBounds(const LpMetadataHeader& header, const LpMetadataTableDescriptor& table) { if (table.offset > header.tables_size) { Loading