Merge "liblp: Add a gtest for sparse image creation."
This commit is contained in:
commit
c53a0e9158
6 changed files with 115 additions and 57 deletions
|
|
@ -51,6 +51,7 @@ cc_test {
|
|||
"liblp",
|
||||
"libbase",
|
||||
"libfs_mgr",
|
||||
"libsparse",
|
||||
],
|
||||
srcs: [
|
||||
"builder_test.cpp",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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));
|
||||
|
|
@ -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)) {
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, in
|
|||
std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
|
||||
size_t size);
|
||||
bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
|
||||
bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry);
|
||||
bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry);
|
||||
|
||||
// These functions assume a valid geometry and slot number.
|
||||
std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue