releasetools: Always write the last block if it's padded.

In BBOTAs if the last block of a DataImage is padded, we should always
write the whole block even for incremental OTAs. Because otherwise the
last block may be skipped if unchanged, but would fail the post-install
verification if it has non-zero contents in the padding bytes.

Bug: 23828506
Change-Id: I4b0af7344d18261258cd48d18c029c089d6ff365
This commit is contained in:
Tao Bao
2015-09-05 20:35:32 -07:00
parent 762746705c
commit 7589e961a7

View File

@@ -106,11 +106,13 @@ class DataImage(Image):
assert not (trim and pad) assert not (trim and pad)
partial = len(self.data) % self.blocksize partial = len(self.data) % self.blocksize
padded = False
if partial > 0: if partial > 0:
if trim: if trim:
self.data = self.data[:-partial] self.data = self.data[:-partial]
elif pad: elif pad:
self.data += '\0' * (self.blocksize - partial) self.data += '\0' * (self.blocksize - partial)
padded = True
else: else:
raise ValueError(("data for DataImage must be multiple of %d bytes " raise ValueError(("data for DataImage must be multiple of %d bytes "
"unless trim or pad is specified") % "unless trim or pad is specified") %
@@ -120,6 +122,15 @@ class DataImage(Image):
self.total_blocks = len(self.data) / self.blocksize self.total_blocks = len(self.data) / self.blocksize
self.care_map = RangeSet(data=(0, self.total_blocks)) self.care_map = RangeSet(data=(0, self.total_blocks))
# When the last block is padded, we always write the whole block even for
# incremental OTAs. Because otherwise the last block may get skipped if
# unchanged for an incremental, but would fail the post-install
# verification if it has non-zero contents in the padding bytes.
# Bug: 23828506
if padded:
self.clobbered_blocks = RangeSet(
data=(self.total_blocks-1, self.total_blocks))
else:
self.clobbered_blocks = RangeSet() self.clobbered_blocks = RangeSet()
self.extended = RangeSet() self.extended = RangeSet()
@@ -127,7 +138,7 @@ class DataImage(Image):
nonzero_blocks = [] nonzero_blocks = []
reference = '\0' * self.blocksize reference = '\0' * self.blocksize
for i in range(self.total_blocks): for i in range(self.total_blocks-1 if padded else self.total_blocks):
d = self.data[i*self.blocksize : (i+1)*self.blocksize] d = self.data[i*self.blocksize : (i+1)*self.blocksize]
if d == reference: if d == reference:
zero_blocks.append(i) zero_blocks.append(i)
@@ -139,13 +150,17 @@ class DataImage(Image):
self.file_map = {"__ZERO": RangeSet(zero_blocks), self.file_map = {"__ZERO": RangeSet(zero_blocks),
"__NONZERO": RangeSet(nonzero_blocks)} "__NONZERO": RangeSet(nonzero_blocks)}
if self.clobbered_blocks:
self.file_map["__COPY"] = self.clobbered_blocks
def ReadRangeSet(self, ranges): def ReadRangeSet(self, ranges):
return [self.data[s*self.blocksize:e*self.blocksize] for (s, e) in ranges] return [self.data[s*self.blocksize:e*self.blocksize] for (s, e) in ranges]
def TotalSha1(self, include_clobbered_blocks=False): def TotalSha1(self, include_clobbered_blocks=False):
# DataImage always carries empty clobbered_blocks, so if not include_clobbered_blocks:
# include_clobbered_blocks can be ignored. ranges = self.care_map.subtract(self.clobbered_blocks)
assert self.clobbered_blocks.size() == 0 return sha1(self.ReadRangeSet(ranges)).hexdigest()
else:
return sha1(self.data).hexdigest() return sha1(self.data).hexdigest()