diff options
Diffstat (limited to 'tools/releasetools/blockimgdiff.py')
| -rw-r--r-- | tools/releasetools/blockimgdiff.py | 220 |
1 files changed, 8 insertions, 212 deletions
diff --git a/tools/releasetools/blockimgdiff.py b/tools/releasetools/blockimgdiff.py index b23eef112c..72f065d19d 100644 --- a/tools/releasetools/blockimgdiff.py +++ b/tools/releasetools/blockimgdiff.py @@ -28,12 +28,12 @@ import sys import threading import zlib from collections import deque, namedtuple, OrderedDict -from hashlib import sha1 import common +from images import EmptyImage from rangelib import RangeSet -__all__ = ["EmptyImage", "DataImage", "BlockImageDiff"] +__all__ = ["BlockImageDiff"] logger = logging.getLogger(__name__) @@ -60,210 +60,6 @@ def compute_patch(srcfile, tgtfile, imgdiff=False): return PatchInfo(imgdiff, f.read()) -class Image(object): - def RangeSha1(self, ranges): - raise NotImplementedError - - def ReadRangeSet(self, ranges): - raise NotImplementedError - - def TotalSha1(self, include_clobbered_blocks=False): - raise NotImplementedError - - def WriteRangeDataToFd(self, ranges, fd): - raise NotImplementedError - - -class EmptyImage(Image): - """A zero-length image.""" - - def __init__(self): - self.blocksize = 4096 - self.care_map = RangeSet() - self.clobbered_blocks = RangeSet() - self.extended = RangeSet() - self.total_blocks = 0 - self.file_map = {} - self.hashtree_info = None - - def RangeSha1(self, ranges): - return sha1().hexdigest() - - def ReadRangeSet(self, ranges): - return () - - def TotalSha1(self, include_clobbered_blocks=False): - # EmptyImage always carries empty clobbered_blocks, so - # include_clobbered_blocks can be ignored. - assert self.clobbered_blocks.size() == 0 - return sha1().hexdigest() - - def WriteRangeDataToFd(self, ranges, fd): - raise ValueError("Can't write data from EmptyImage to file") - - -class DataImage(Image): - """An image wrapped around a single string of data.""" - - def __init__(self, data, trim=False, pad=False): - self.data = data - self.blocksize = 4096 - - assert not (trim and pad) - - partial = len(self.data) % self.blocksize - padded = False - if partial > 0: - if trim: - self.data = self.data[:-partial] - elif pad: - self.data += '\0' * (self.blocksize - partial) - padded = True - else: - raise ValueError(("data for DataImage must be multiple of %d bytes " - "unless trim or pad is specified") % - (self.blocksize,)) - - assert len(self.data) % self.blocksize == 0 - - self.total_blocks = len(self.data) / self.blocksize - 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: - clobbered_blocks = [self.total_blocks-1, self.total_blocks] - else: - clobbered_blocks = [] - self.clobbered_blocks = clobbered_blocks - self.extended = RangeSet() - - zero_blocks = [] - nonzero_blocks = [] - reference = '\0' * self.blocksize - - for i in range(self.total_blocks-1 if padded else self.total_blocks): - d = self.data[i*self.blocksize : (i+1)*self.blocksize] - if d == reference: - zero_blocks.append(i) - zero_blocks.append(i+1) - else: - nonzero_blocks.append(i) - nonzero_blocks.append(i+1) - - assert zero_blocks or nonzero_blocks or clobbered_blocks - - self.file_map = dict() - if zero_blocks: - self.file_map["__ZERO"] = RangeSet(data=zero_blocks) - if nonzero_blocks: - self.file_map["__NONZERO"] = RangeSet(data=nonzero_blocks) - if clobbered_blocks: - self.file_map["__COPY"] = RangeSet(data=clobbered_blocks) - - def _GetRangeData(self, ranges): - for s, e in ranges: - yield self.data[s*self.blocksize:e*self.blocksize] - - def RangeSha1(self, ranges): - h = sha1() - for data in self._GetRangeData(ranges): # pylint: disable=not-an-iterable - h.update(data) - return h.hexdigest() - - def ReadRangeSet(self, ranges): - return list(self._GetRangeData(ranges)) - - def TotalSha1(self, include_clobbered_blocks=False): - if not include_clobbered_blocks: - return self.RangeSha1(self.care_map.subtract(self.clobbered_blocks)) - else: - return sha1(self.data).hexdigest() - - def WriteRangeDataToFd(self, ranges, fd): - for data in self._GetRangeData(ranges): # pylint: disable=not-an-iterable - fd.write(data) - - -class FileImage(Image): - """An image wrapped around a raw image file.""" - - def __init__(self, path, hashtree_info_generator=None): - self.path = path - self.blocksize = 4096 - self._file_size = os.path.getsize(self.path) - self._file = open(self.path, 'r') - - if self._file_size % self.blocksize != 0: - raise ValueError("Size of file %s must be multiple of %d bytes, but is %d" - % self.path, self.blocksize, self._file_size) - - self.total_blocks = self._file_size / self.blocksize - self.care_map = RangeSet(data=(0, self.total_blocks)) - self.clobbered_blocks = RangeSet() - self.extended = RangeSet() - - self.generator_lock = threading.Lock() - - self.hashtree_info = None - if hashtree_info_generator: - self.hashtree_info = hashtree_info_generator.Generate(self) - - zero_blocks = [] - nonzero_blocks = [] - reference = '\0' * self.blocksize - - for i in range(self.total_blocks): - d = self._file.read(self.blocksize) - if d == reference: - zero_blocks.append(i) - zero_blocks.append(i+1) - else: - nonzero_blocks.append(i) - nonzero_blocks.append(i+1) - - assert zero_blocks or nonzero_blocks - - self.file_map = {} - if zero_blocks: - self.file_map["__ZERO"] = RangeSet(data=zero_blocks) - if nonzero_blocks: - self.file_map["__NONZERO"] = RangeSet(data=nonzero_blocks) - if self.hashtree_info: - self.file_map["__HASHTREE"] = self.hashtree_info.hashtree_range - - def __del__(self): - self._file.close() - - def _GetRangeData(self, ranges): - # Use a lock to protect the generator so that we will not run two - # instances of this generator on the same object simultaneously. - with self.generator_lock: - for s, e in ranges: - self._file.seek(s * self.blocksize) - for _ in range(s, e): - yield self._file.read(self.blocksize) - - def RangeSha1(self, ranges): - h = sha1() - for data in self._GetRangeData(ranges): # pylint: disable=not-an-iterable - h.update(data) - return h.hexdigest() - - def ReadRangeSet(self, ranges): - return list(self._GetRangeData(ranges)) - - def TotalSha1(self, include_clobbered_blocks=False): - assert not self.clobbered_blocks - return self.RangeSha1(self.care_map) - - def WriteRangeDataToFd(self, ranges, fd): - for data in self._GetRangeData(ranges): # pylint: disable=not-an-iterable - fd.write(data) - - class Transfer(object): def __init__(self, tgt_name, src_name, tgt_ranges, src_ranges, tgt_sha1, src_sha1, style, by_id): @@ -391,7 +187,7 @@ class ImgdiffStats(object): def print_header(header, separator): logger.info(header) - logger.info(separator * len(header) + '\n') + logger.info('%s\n', separator * len(header)) print_header(' Imgdiff Stats Report ', '=') for key in self.REASONS: @@ -779,7 +575,7 @@ class BlockImageDiff(object): out.insert(2, "0\n") out.insert(3, str(max_stashed_blocks) + "\n") - with open(prefix + ".transfer.list", "wb") as f: + with open(prefix + ".transfer.list", "w") as f: for i in out: f.write(i) @@ -1009,7 +805,7 @@ class BlockImageDiff(object): # - we write every block we care about exactly once. # Start with no blocks having been touched yet. - touched = array.array("B", "\0" * self.tgt.total_blocks) + touched = array.array("B", b"\0" * self.tgt.total_blocks) # Imagine processing the transfers in order. for xf in self.transfers: @@ -1671,8 +1467,8 @@ class BlockImageDiff(object): split_tgt_size = int(info[1]) assert split_tgt_size % 4096 == 0 - assert split_tgt_size / 4096 <= tgt_remain.size() - split_tgt_ranges = tgt_remain.first(split_tgt_size / 4096) + assert split_tgt_size // 4096 <= tgt_remain.size() + split_tgt_ranges = tgt_remain.first(split_tgt_size // 4096) tgt_remain = tgt_remain.subtract(split_tgt_ranges) # Find the split_src_ranges within the image file from its relative @@ -1744,7 +1540,7 @@ class BlockImageDiff(object): lines) for index, (patch_start, patch_length, split_tgt_ranges, split_src_ranges) in enumerate(split_info_list): - with open(patch_file) as f: + with open(patch_file, 'rb') as f: f.seek(patch_start) patch_content = f.read(patch_length) |