Merge "releasetools: Allow generating BBOTA for images with shared blocks."

am: 9452d84b97

Change-Id: Iab1c1e4f25f67fbf9eebb9ec8a4ff0ad7e05fd13
This commit is contained in:
Tao Bao 2018-02-13 22:34:25 +00:00 committed by android-build-merger
commit 6c453193a1
4 changed files with 63 additions and 11 deletions

View file

@ -272,6 +272,7 @@ class ImgdiffStats(object):
# Reasons for not applying imgdiff on APKs.
SKIPPED_TRIMMED = "Not used imgdiff due to trimmed RangeSet"
SKIPPED_NONMONOTONIC = "Not used imgdiff due to having non-monotonic ranges"
SKIPPED_SHARED_BLOCKS = "Not used imgdiff due to using shared blocks"
SKIPPED_INCOMPLETE = "Not used imgdiff due to incomplete RangeSet"
# The list of valid reasons, which will also be the dumped order in a report.
@ -280,6 +281,7 @@ class ImgdiffStats(object):
USED_IMGDIFF_LARGE_APK,
SKIPPED_TRIMMED,
SKIPPED_NONMONOTONIC,
SKIPPED_SHARED_BLOCKS,
SKIPPED_INCOMPLETE,
)
@ -414,6 +416,7 @@ class BlockImageDiff(object):
- The file type is supported by imgdiff;
- The source and target blocks are monotonic (i.e. the data is stored with
blocks in increasing order);
- Both files don't contain shared blocks;
- Both files have complete lists of blocks;
- We haven't removed any blocks from the source set.
@ -437,6 +440,11 @@ class BlockImageDiff(object):
self.imgdiff_stats.Log(name, ImgdiffStats.SKIPPED_NONMONOTONIC)
return False
if (tgt_ranges.extra.get('uses_shared_blocks') or
src_ranges.extra.get('uses_shared_blocks')):
self.imgdiff_stats.Log(name, ImgdiffStats.SKIPPED_SHARED_BLOCKS)
return False
if tgt_ranges.extra.get('incomplete') or src_ranges.extra.get('incomplete'):
self.imgdiff_stats.Log(name, ImgdiffStats.SKIPPED_INCOMPLETE)
return False

View file

@ -625,7 +625,7 @@ def UnzipTemp(filename, pattern=None):
return tmp, zipfile.ZipFile(filename, "r")
def GetSparseImage(which, tmpdir, input_zip):
def GetSparseImage(which, tmpdir, input_zip, allow_shared_blocks):
"""Returns a SparseImage object suitable for passing to BlockImageDiff.
This function loads the specified sparse image from the given path, and
@ -637,6 +637,7 @@ def GetSparseImage(which, tmpdir, input_zip):
which: The partition name, which must be "system" or "vendor".
tmpdir: The directory that contains the prebuilt image and block map file.
input_zip: The target-files ZIP archive.
allow_shared_blocks: Whether having shared blocks is allowed.
Returns:
A SparseImage object, with file_map info loaded.
@ -655,7 +656,8 @@ def GetSparseImage(which, tmpdir, input_zip):
# unconditionally. Note that they are still part of care_map. (Bug: 20939131)
clobbered_blocks = "0"
image = sparse_img.SparseImage(path, mappath, clobbered_blocks)
image = sparse_img.SparseImage(path, mappath, clobbered_blocks,
allow_shared_blocks=allow_shared_blocks)
# block.map may contain less blocks, because mke2fs may skip allocating blocks
# if they contain all zeros. We can't reconstruct such a file from its block
@ -669,6 +671,13 @@ def GetSparseImage(which, tmpdir, input_zip):
info = input_zip.getinfo(arcname)
ranges = image.file_map[entry]
# If a RangeSet has been tagged as using shared blocks while loading the
# image, its block list must be already incomplete due to that reason. Don't
# give it 'incomplete' tag to avoid messing up the imgdiff stats.
if ranges.extra.get('uses_shared_blocks'):
continue
if RoundUpTo4K(info.file_size) > ranges.size() * 4096:
ranges.extra['incomplete'] = True

View file

@ -786,11 +786,15 @@ else if get_stage("%(bcb_dev)s") == "3/3" then
script.ShowProgress(system_progress, 0)
# See the notes in WriteBlockIncrementalOTAPackage().
allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
# Full OTA is done as an "incremental" against an empty source image. This
# has the effect of writing new data from the package to the entire
# partition, but lets us reuse the updater code that writes incrementals to
# do it.
system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip)
system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip,
allow_shared_blocks)
system_tgt.ResetFileMap()
system_diff = common.BlockDifference("system", system_tgt, src=None)
system_diff.WriteScript(script, output_zip)
@ -801,7 +805,8 @@ else if get_stage("%(bcb_dev)s") == "3/3" then
if HasVendorPartition(input_zip):
script.ShowProgress(0.1, 0)
vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip)
vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip,
allow_shared_blocks)
vendor_tgt.ResetFileMap()
vendor_diff = common.BlockDifference("vendor", vendor_tgt)
vendor_diff.WriteScript(script, output_zip)
@ -978,8 +983,16 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
target_recovery = common.GetBootableImage(
"/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip)
system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip)
# When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
# shared blocks (i.e. some blocks will show up in multiple files' block
# list). We can only allocate such shared blocks to the first "owner", and
# disable imgdiff for all later occurrences.
allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
target_info.get('ext4_share_dup_blocks') == "true")
system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
allow_shared_blocks)
system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
allow_shared_blocks)
blockimgdiff_version = max(
int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
@ -1004,8 +1017,10 @@ def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
if HasVendorPartition(target_zip):
if not HasVendorPartition(source_zip):
raise RuntimeError("can't generate incremental that adds /vendor")
vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip)
vendor_tgt = common.GetSparseImage("vendor", OPTIONS.target_tmp, target_zip)
vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
allow_shared_blocks)
vendor_tgt = common.GetSparseImage("vendor", OPTIONS.target_tmp, target_zip,
allow_shared_blocks)
# Check first block of vendor partition for remount R/W only if
# disk type is ext4

View file

@ -33,7 +33,7 @@ class SparseImage(object):
"""
def __init__(self, simg_fn, file_map_fn=None, clobbered_blocks=None,
mode="rb", build_map=True):
mode="rb", build_map=True, allow_shared_blocks=False):
self.simg_f = f = open(simg_fn, mode)
header_bin = f.read(28)
@ -129,7 +129,8 @@ class SparseImage(object):
self.extended = extended
if file_map_fn:
self.LoadFileBlockMap(file_map_fn, self.clobbered_blocks)
self.LoadFileBlockMap(file_map_fn, self.clobbered_blocks,
allow_shared_blocks)
else:
self.file_map = {"__DATA": self.care_map}
@ -209,7 +210,14 @@ class SparseImage(object):
yield fill_data * (this_read * (self.blocksize >> 2))
to_read -= this_read
def LoadFileBlockMap(self, fn, clobbered_blocks):
def LoadFileBlockMap(self, fn, clobbered_blocks, allow_shared_blocks):
"""Loads the given block map file.
Args:
fn: The filename of the block map file.
clobbered_blocks: A RangeSet instance for the clobbered blocks.
allow_shared_blocks: Whether having shared blocks is allowed.
"""
remaining = self.care_map
self.file_map = out = {}
@ -217,6 +225,18 @@ class SparseImage(object):
for line in f:
fn, ranges = line.split(None, 1)
ranges = rangelib.RangeSet.parse(ranges)
if allow_shared_blocks:
# Find the shared blocks that have been claimed by others.
shared_blocks = ranges.subtract(remaining)
if shared_blocks:
ranges = ranges.subtract(shared_blocks)
if not ranges:
continue
# Tag the entry so that we can skip applying imgdiff on this file.
ranges.extra['uses_shared_blocks'] = True
out[fn] = ranges
assert ranges.size() == ranges.intersect(remaining).size()