Fix bug where uncompressed files do not get extent info populated
Current parser skips parsing uncompressed files. As a result, all
uncompressed files are recognized as unallocated blocks in filesystem,
and are diffed in 1 context. This caused large OTA size bloat. Fix by
popualting extent info for uncompressed files.
Test: Build OTA between two uncompressed EROFS images
Bug: 233238557
Change-Id: Ie3325c72ce65e185ed6153577553718aa9a7baa2
diff --git a/payload_generator/erofs_filesystem.cc b/payload_generator/erofs_filesystem.cc
index 677b473..bf10d8c 100644
--- a/payload_generator/erofs_filesystem.cc
+++ b/payload_generator/erofs_filesystem.cc
@@ -76,28 +76,39 @@
block.m_algorithmformat != Z_EROFS_COMPRESSION_SHIFTED;
}
-static void FillCompressedBlockInfo(FilesystemInterface::File* p_file,
- std::string_view image_filename,
- struct erofs_inode* inode) {
+static void FillExtentInfo(FilesystemInterface::File* p_file,
+ std::string_view image_filename,
+ struct erofs_inode* inode) {
auto& file = *p_file;
- if (!file.is_compressed) {
- return;
- }
struct erofs_map_blocks block {};
block.m_la = 0;
block.index = UINT_MAX;
- const erofs_off_t uncompressed_size = file.file_stat.st_size;
auto& compressed_blocks = file.compressed_file_info.blocks;
auto last_pa = block.m_pa;
auto last_plen = 0;
- while (block.m_la < uncompressed_size) {
+ LOG(INFO) << file.name << ", isize: " << inode->i_size;
+ while (block.m_la < inode->i_size) {
auto error = ErofsMapBlocks(inode, &block, EROFS_GET_BLOCKS_FIEMAP);
if (error) {
LOG(FATAL) << "Failed to map blocks for " << file.name << " in "
<< image_filename;
}
+ if (block.m_pa % kBlockSize != 0) {
+ // EROFS might put the last block on unalighed addresses, because the last
+ // block is often < 1 full block size. That is fine, we can usually
+ // tolerate small amount of data being unaligned.
+ if (block.m_llen >= kBlockSize ||
+ block.m_la + block.m_llen != inode->i_size) {
+ LOG(ERROR) << "File `" << file.name
+ << "` has unaligned blocks: at physical byte offset: "
+ << block.m_pa << ", "
+ << " length: " << block.m_plen
+ << ", logical offset: " << block.m_la;
+ }
+ break;
+ }
// Certain uncompressed blocks have physical size > logical size. Usually
// the physical block contains bunch of trailing zeros. Include thees
// bytes in the logical size as well.
@@ -116,16 +127,18 @@
} else {
last_plen += block.m_plen;
}
- // If logical size and physical size are the same, this block is
- // uncompressed. Join consecutive uncompressed blocks to save a bit memory
- // storing metadata.
- if (block.m_llen == block.m_plen && !compressed_blocks.empty() &&
- !compressed_blocks.back().IsCompressed()) {
- compressed_blocks.back().compressed_length += block.m_llen;
- compressed_blocks.back().uncompressed_length += block.m_llen;
- } else {
- compressed_blocks.push_back(
- CompressedBlock(block.m_la, block.m_plen, block.m_llen));
+ if (file.is_compressed) {
+ // If logical size and physical size are the same, this block is
+ // uncompressed. Join consecutive uncompressed blocks to save a bit memory
+ // storing metadata.
+ if (block.m_llen == block.m_plen && !compressed_blocks.empty() &&
+ !compressed_blocks.back().IsCompressed()) {
+ compressed_blocks.back().compressed_length += block.m_llen;
+ compressed_blocks.back().uncompressed_length += block.m_llen;
+ } else {
+ compressed_blocks.push_back(
+ CompressedBlock(block.m_la, block.m_plen, block.m_llen));
+ }
}
block.m_la += block.m_llen;
@@ -154,24 +167,28 @@
PLOG(INFO) << "Failed to open " << filename;
return nullptr;
}
- DEFER { dev_close(); };
+ DEFER {
+ dev_close();
+ };
if (const auto err = erofs_read_superblock(); err) {
PLOG(INFO) << "Failed to parse " << filename << " as EROFS image";
return nullptr;
}
- struct stat st;
+ struct stat st {};
if (const auto err = fstat(erofs_devfd, &st); err) {
PLOG(ERROR) << "Failed to stat() " << filename;
return nullptr;
}
const time_t time = sbi.build_time;
- LOG(INFO) << "Parsed EROFS image of size " << st.st_size << " built in "
- << ctime(&time) << " " << filename;
std::vector<File> files;
if (!ErofsFilesystem::GetFiles(filename, &files, algo)) {
return nullptr;
}
+
+ LOG(INFO) << "Parsed EROFS image of size " << st.st_size << " built in "
+ << ctime(&time) << " " << filename
+ << ", number of files: " << files.size();
LOG(INFO) << "Using compression algo " << algo << " for " << filename;
// private ctor doesn't work with make_unique
return std::unique_ptr<ErofsFilesystem>(
@@ -191,7 +208,7 @@
if (info.ctx.de_ftype != EROFS_FT_REG_FILE) {
return 0;
}
- struct erofs_inode inode;
+ struct erofs_inode inode {};
inode.nid = info.ctx.de_nid;
int err = erofs_read_inode_from_disk(&inode);
if (err) {
@@ -225,7 +242,7 @@
file.file_stat.st_size = uncompressed_size;
file.file_stat.st_ino = inode.nid;
- FillCompressedBlockInfo(&file, filename, &inode);
+ FillExtentInfo(&file, filename, &inode);
file.compressed_file_info.algo = algo;
files->emplace_back(std::move(file));