1*9e94795aSAndroid Build Coastguard Worker#!/usr/bin/env python 2*9e94795aSAndroid Build Coastguard Worker# 3*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2014 The Android Open Source Project 4*9e94795aSAndroid Build Coastguard Worker# 5*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*9e94795aSAndroid Build Coastguard Worker# 9*9e94795aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*9e94795aSAndroid Build Coastguard Worker# 11*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*9e94795aSAndroid Build Coastguard Worker# limitations under the License. 16*9e94795aSAndroid Build Coastguard Worker 17*9e94795aSAndroid Build Coastguard Workerfrom __future__ import print_function 18*9e94795aSAndroid Build Coastguard Worker 19*9e94795aSAndroid Build Coastguard Workerimport argparse 20*9e94795aSAndroid Build Coastguard Workerimport bisect 21*9e94795aSAndroid Build Coastguard Workerimport logging 22*9e94795aSAndroid Build Coastguard Workerimport os 23*9e94795aSAndroid Build Coastguard Workerimport struct 24*9e94795aSAndroid Build Coastguard Workerimport threading 25*9e94795aSAndroid Build Coastguard Workerfrom hashlib import sha1 26*9e94795aSAndroid Build Coastguard Worker 27*9e94795aSAndroid Build Coastguard Workerimport rangelib 28*9e94795aSAndroid Build Coastguard Worker 29*9e94795aSAndroid Build Coastguard Workerlogger = logging.getLogger(__name__) 30*9e94795aSAndroid Build Coastguard Worker 31*9e94795aSAndroid Build Coastguard Worker 32*9e94795aSAndroid Build Coastguard Workerclass SparseImage(object): 33*9e94795aSAndroid Build Coastguard Worker """Wraps a sparse image file into an image object. 34*9e94795aSAndroid Build Coastguard Worker 35*9e94795aSAndroid Build Coastguard Worker Wraps a sparse image file (and optional file map and clobbered_blocks) into 36*9e94795aSAndroid Build Coastguard Worker an image object suitable for passing to BlockImageDiff. file_map contains 37*9e94795aSAndroid Build Coastguard Worker the mapping between files and their blocks. clobbered_blocks contains the set 38*9e94795aSAndroid Build Coastguard Worker of blocks that should be always written to the target regardless of the old 39*9e94795aSAndroid Build Coastguard Worker contents (i.e. copying instead of patching). clobbered_blocks should be in 40*9e94795aSAndroid Build Coastguard Worker the form of a string like "0" or "0 1-5 8". 41*9e94795aSAndroid Build Coastguard Worker """ 42*9e94795aSAndroid Build Coastguard Worker 43*9e94795aSAndroid Build Coastguard Worker def __init__(self, simg_fn, file_map_fn=None, clobbered_blocks=None, 44*9e94795aSAndroid Build Coastguard Worker mode="rb", build_map=True, allow_shared_blocks=False): 45*9e94795aSAndroid Build Coastguard Worker self.simg_f = f = open(simg_fn, mode) 46*9e94795aSAndroid Build Coastguard Worker 47*9e94795aSAndroid Build Coastguard Worker header_bin = f.read(28) 48*9e94795aSAndroid Build Coastguard Worker header = struct.unpack("<I4H4I", header_bin) 49*9e94795aSAndroid Build Coastguard Worker 50*9e94795aSAndroid Build Coastguard Worker magic = header[0] 51*9e94795aSAndroid Build Coastguard Worker major_version = header[1] 52*9e94795aSAndroid Build Coastguard Worker minor_version = header[2] 53*9e94795aSAndroid Build Coastguard Worker file_hdr_sz = header[3] 54*9e94795aSAndroid Build Coastguard Worker chunk_hdr_sz = header[4] 55*9e94795aSAndroid Build Coastguard Worker self.blocksize = blk_sz = header[5] 56*9e94795aSAndroid Build Coastguard Worker self.total_blocks = total_blks = header[6] 57*9e94795aSAndroid Build Coastguard Worker self.total_chunks = total_chunks = header[7] 58*9e94795aSAndroid Build Coastguard Worker 59*9e94795aSAndroid Build Coastguard Worker if magic != 0xED26FF3A: 60*9e94795aSAndroid Build Coastguard Worker raise ValueError("Magic should be 0xED26FF3A but is 0x%08X" % (magic,)) 61*9e94795aSAndroid Build Coastguard Worker if major_version != 1 or minor_version != 0: 62*9e94795aSAndroid Build Coastguard Worker raise ValueError("I know about version 1.0, but this is version %u.%u" % 63*9e94795aSAndroid Build Coastguard Worker (major_version, minor_version)) 64*9e94795aSAndroid Build Coastguard Worker if file_hdr_sz != 28: 65*9e94795aSAndroid Build Coastguard Worker raise ValueError("File header size was expected to be 28, but is %u." % 66*9e94795aSAndroid Build Coastguard Worker (file_hdr_sz,)) 67*9e94795aSAndroid Build Coastguard Worker if chunk_hdr_sz != 12: 68*9e94795aSAndroid Build Coastguard Worker raise ValueError("Chunk header size was expected to be 12, but is %u." % 69*9e94795aSAndroid Build Coastguard Worker (chunk_hdr_sz,)) 70*9e94795aSAndroid Build Coastguard Worker 71*9e94795aSAndroid Build Coastguard Worker logger.info( 72*9e94795aSAndroid Build Coastguard Worker "Total of %u %u-byte output blocks in %u input chunks.", total_blks, 73*9e94795aSAndroid Build Coastguard Worker blk_sz, total_chunks) 74*9e94795aSAndroid Build Coastguard Worker 75*9e94795aSAndroid Build Coastguard Worker if not build_map: 76*9e94795aSAndroid Build Coastguard Worker return 77*9e94795aSAndroid Build Coastguard Worker 78*9e94795aSAndroid Build Coastguard Worker pos = 0 # in blocks 79*9e94795aSAndroid Build Coastguard Worker care_data = [] 80*9e94795aSAndroid Build Coastguard Worker self.offset_map = offset_map = [] 81*9e94795aSAndroid Build Coastguard Worker self.clobbered_blocks = rangelib.RangeSet(data=clobbered_blocks) 82*9e94795aSAndroid Build Coastguard Worker 83*9e94795aSAndroid Build Coastguard Worker for _ in range(total_chunks): 84*9e94795aSAndroid Build Coastguard Worker header_bin = f.read(12) 85*9e94795aSAndroid Build Coastguard Worker header = struct.unpack("<2H2I", header_bin) 86*9e94795aSAndroid Build Coastguard Worker chunk_type = header[0] 87*9e94795aSAndroid Build Coastguard Worker chunk_sz = header[2] 88*9e94795aSAndroid Build Coastguard Worker total_sz = header[3] 89*9e94795aSAndroid Build Coastguard Worker data_sz = total_sz - 12 90*9e94795aSAndroid Build Coastguard Worker 91*9e94795aSAndroid Build Coastguard Worker if chunk_type == 0xCAC1: 92*9e94795aSAndroid Build Coastguard Worker if data_sz != (chunk_sz * blk_sz): 93*9e94795aSAndroid Build Coastguard Worker raise ValueError( 94*9e94795aSAndroid Build Coastguard Worker "Raw chunk input size (%u) does not match output size (%u)" % 95*9e94795aSAndroid Build Coastguard Worker (data_sz, chunk_sz * blk_sz)) 96*9e94795aSAndroid Build Coastguard Worker else: 97*9e94795aSAndroid Build Coastguard Worker care_data.append(pos) 98*9e94795aSAndroid Build Coastguard Worker care_data.append(pos + chunk_sz) 99*9e94795aSAndroid Build Coastguard Worker offset_map.append((pos, chunk_sz, f.tell(), None)) 100*9e94795aSAndroid Build Coastguard Worker pos += chunk_sz 101*9e94795aSAndroid Build Coastguard Worker f.seek(data_sz, os.SEEK_CUR) 102*9e94795aSAndroid Build Coastguard Worker 103*9e94795aSAndroid Build Coastguard Worker elif chunk_type == 0xCAC2: 104*9e94795aSAndroid Build Coastguard Worker fill_data = f.read(4) 105*9e94795aSAndroid Build Coastguard Worker care_data.append(pos) 106*9e94795aSAndroid Build Coastguard Worker care_data.append(pos + chunk_sz) 107*9e94795aSAndroid Build Coastguard Worker offset_map.append((pos, chunk_sz, None, fill_data)) 108*9e94795aSAndroid Build Coastguard Worker pos += chunk_sz 109*9e94795aSAndroid Build Coastguard Worker 110*9e94795aSAndroid Build Coastguard Worker elif chunk_type == 0xCAC3: 111*9e94795aSAndroid Build Coastguard Worker if data_sz != 0: 112*9e94795aSAndroid Build Coastguard Worker raise ValueError("Don't care chunk input size is non-zero (%u)" % 113*9e94795aSAndroid Build Coastguard Worker (data_sz)) 114*9e94795aSAndroid Build Coastguard Worker 115*9e94795aSAndroid Build Coastguard Worker pos += chunk_sz 116*9e94795aSAndroid Build Coastguard Worker 117*9e94795aSAndroid Build Coastguard Worker elif chunk_type == 0xCAC4: 118*9e94795aSAndroid Build Coastguard Worker raise ValueError("CRC32 chunks are not supported") 119*9e94795aSAndroid Build Coastguard Worker 120*9e94795aSAndroid Build Coastguard Worker else: 121*9e94795aSAndroid Build Coastguard Worker raise ValueError("Unknown chunk type 0x%04X not supported" % 122*9e94795aSAndroid Build Coastguard Worker (chunk_type,)) 123*9e94795aSAndroid Build Coastguard Worker 124*9e94795aSAndroid Build Coastguard Worker self.generator_lock = threading.Lock() 125*9e94795aSAndroid Build Coastguard Worker 126*9e94795aSAndroid Build Coastguard Worker self.care_map = rangelib.RangeSet(care_data) 127*9e94795aSAndroid Build Coastguard Worker self.offset_index = [i[0] for i in offset_map] 128*9e94795aSAndroid Build Coastguard Worker 129*9e94795aSAndroid Build Coastguard Worker # Bug: 20881595 130*9e94795aSAndroid Build Coastguard Worker # Introduce extended blocks as a workaround for the bug. dm-verity may 131*9e94795aSAndroid Build Coastguard Worker # touch blocks that are not in the care_map due to block device 132*9e94795aSAndroid Build Coastguard Worker # read-ahead. It will fail if such blocks contain non-zeroes. We zero out 133*9e94795aSAndroid Build Coastguard Worker # the extended blocks explicitly to avoid dm-verity failures. 512 blocks 134*9e94795aSAndroid Build Coastguard Worker # are the maximum read-ahead we configure for dm-verity block devices. 135*9e94795aSAndroid Build Coastguard Worker extended = self.care_map.extend(512) 136*9e94795aSAndroid Build Coastguard Worker all_blocks = rangelib.RangeSet(data=(0, self.total_blocks)) 137*9e94795aSAndroid Build Coastguard Worker extended = extended.intersect(all_blocks).subtract(self.care_map) 138*9e94795aSAndroid Build Coastguard Worker self.extended = extended 139*9e94795aSAndroid Build Coastguard Worker 140*9e94795aSAndroid Build Coastguard Worker if file_map_fn: 141*9e94795aSAndroid Build Coastguard Worker self.LoadFileBlockMap(file_map_fn, self.clobbered_blocks, 142*9e94795aSAndroid Build Coastguard Worker allow_shared_blocks) 143*9e94795aSAndroid Build Coastguard Worker else: 144*9e94795aSAndroid Build Coastguard Worker self.file_map = {"__DATA": self.care_map} 145*9e94795aSAndroid Build Coastguard Worker 146*9e94795aSAndroid Build Coastguard Worker def AppendFillChunk(self, data, blocks): 147*9e94795aSAndroid Build Coastguard Worker f = self.simg_f 148*9e94795aSAndroid Build Coastguard Worker 149*9e94795aSAndroid Build Coastguard Worker # Append a fill chunk 150*9e94795aSAndroid Build Coastguard Worker f.seek(0, os.SEEK_END) 151*9e94795aSAndroid Build Coastguard Worker f.write(struct.pack("<2H3I", 0xCAC2, 0, blocks, 16, data)) 152*9e94795aSAndroid Build Coastguard Worker 153*9e94795aSAndroid Build Coastguard Worker # Update the sparse header 154*9e94795aSAndroid Build Coastguard Worker self.total_blocks += blocks 155*9e94795aSAndroid Build Coastguard Worker self.total_chunks += 1 156*9e94795aSAndroid Build Coastguard Worker 157*9e94795aSAndroid Build Coastguard Worker f.seek(16, os.SEEK_SET) 158*9e94795aSAndroid Build Coastguard Worker f.write(struct.pack("<2I", self.total_blocks, self.total_chunks)) 159*9e94795aSAndroid Build Coastguard Worker 160*9e94795aSAndroid Build Coastguard Worker def RangeSha1(self, ranges): 161*9e94795aSAndroid Build Coastguard Worker h = sha1() 162*9e94795aSAndroid Build Coastguard Worker for data in self._GetRangeData(ranges): 163*9e94795aSAndroid Build Coastguard Worker h.update(data) 164*9e94795aSAndroid Build Coastguard Worker return h.hexdigest() 165*9e94795aSAndroid Build Coastguard Worker 166*9e94795aSAndroid Build Coastguard Worker def ReadRangeSet(self, ranges): 167*9e94795aSAndroid Build Coastguard Worker return [d for d in self._GetRangeData(ranges)] 168*9e94795aSAndroid Build Coastguard Worker 169*9e94795aSAndroid Build Coastguard Worker def ReadBlocks(self, start=0, num_blocks=None): 170*9e94795aSAndroid Build Coastguard Worker if num_blocks is None: 171*9e94795aSAndroid Build Coastguard Worker num_blocks = self.total_blocks 172*9e94795aSAndroid Build Coastguard Worker return self._GetRangeData([(start, start + num_blocks)]) 173*9e94795aSAndroid Build Coastguard Worker 174*9e94795aSAndroid Build Coastguard Worker def TotalSha1(self, include_clobbered_blocks=False): 175*9e94795aSAndroid Build Coastguard Worker """Return the SHA-1 hash of all data in the 'care' regions. 176*9e94795aSAndroid Build Coastguard Worker 177*9e94795aSAndroid Build Coastguard Worker If include_clobbered_blocks is True, it returns the hash including the 178*9e94795aSAndroid Build Coastguard Worker clobbered_blocks.""" 179*9e94795aSAndroid Build Coastguard Worker ranges = self.care_map 180*9e94795aSAndroid Build Coastguard Worker if not include_clobbered_blocks: 181*9e94795aSAndroid Build Coastguard Worker ranges = ranges.subtract(self.clobbered_blocks) 182*9e94795aSAndroid Build Coastguard Worker return self.RangeSha1(ranges) 183*9e94795aSAndroid Build Coastguard Worker 184*9e94795aSAndroid Build Coastguard Worker def WriteRangeDataToFd(self, ranges, fd): 185*9e94795aSAndroid Build Coastguard Worker for data in self._GetRangeData(ranges): 186*9e94795aSAndroid Build Coastguard Worker fd.write(data) 187*9e94795aSAndroid Build Coastguard Worker 188*9e94795aSAndroid Build Coastguard Worker def _GetRangeData(self, ranges): 189*9e94795aSAndroid Build Coastguard Worker """Generator that produces all the image data in 'ranges'. The 190*9e94795aSAndroid Build Coastguard Worker number of individual pieces returned is arbitrary (and in 191*9e94795aSAndroid Build Coastguard Worker particular is not necessarily equal to the number of ranges in 192*9e94795aSAndroid Build Coastguard Worker 'ranges'. 193*9e94795aSAndroid Build Coastguard Worker 194*9e94795aSAndroid Build Coastguard Worker Use a lock to protect the generator so that we will not run two 195*9e94795aSAndroid Build Coastguard Worker instances of this generator on the same object simultaneously.""" 196*9e94795aSAndroid Build Coastguard Worker 197*9e94795aSAndroid Build Coastguard Worker f = self.simg_f 198*9e94795aSAndroid Build Coastguard Worker with self.generator_lock: 199*9e94795aSAndroid Build Coastguard Worker for s, e in ranges: 200*9e94795aSAndroid Build Coastguard Worker to_read = e-s 201*9e94795aSAndroid Build Coastguard Worker idx = bisect.bisect_right(self.offset_index, s) - 1 202*9e94795aSAndroid Build Coastguard Worker chunk_start, chunk_len, filepos, fill_data = self.offset_map[idx] 203*9e94795aSAndroid Build Coastguard Worker 204*9e94795aSAndroid Build Coastguard Worker # for the first chunk we may be starting partway through it. 205*9e94795aSAndroid Build Coastguard Worker remain = chunk_len - (s - chunk_start) 206*9e94795aSAndroid Build Coastguard Worker this_read = min(remain, to_read) 207*9e94795aSAndroid Build Coastguard Worker if filepos is not None: 208*9e94795aSAndroid Build Coastguard Worker p = filepos + ((s - chunk_start) * self.blocksize) 209*9e94795aSAndroid Build Coastguard Worker f.seek(p, os.SEEK_SET) 210*9e94795aSAndroid Build Coastguard Worker yield f.read(this_read * self.blocksize) 211*9e94795aSAndroid Build Coastguard Worker else: 212*9e94795aSAndroid Build Coastguard Worker yield fill_data * (this_read * (self.blocksize >> 2)) 213*9e94795aSAndroid Build Coastguard Worker to_read -= this_read 214*9e94795aSAndroid Build Coastguard Worker 215*9e94795aSAndroid Build Coastguard Worker while to_read > 0: 216*9e94795aSAndroid Build Coastguard Worker # continue with following chunks if this range spans multiple chunks. 217*9e94795aSAndroid Build Coastguard Worker idx += 1 218*9e94795aSAndroid Build Coastguard Worker chunk_start, chunk_len, filepos, fill_data = self.offset_map[idx] 219*9e94795aSAndroid Build Coastguard Worker this_read = min(chunk_len, to_read) 220*9e94795aSAndroid Build Coastguard Worker if filepos is not None: 221*9e94795aSAndroid Build Coastguard Worker f.seek(filepos, os.SEEK_SET) 222*9e94795aSAndroid Build Coastguard Worker yield f.read(this_read * self.blocksize) 223*9e94795aSAndroid Build Coastguard Worker else: 224*9e94795aSAndroid Build Coastguard Worker yield fill_data * (this_read * (self.blocksize >> 2)) 225*9e94795aSAndroid Build Coastguard Worker to_read -= this_read 226*9e94795aSAndroid Build Coastguard Worker 227*9e94795aSAndroid Build Coastguard Worker def LoadFileBlockMap(self, fn, clobbered_blocks, allow_shared_blocks): 228*9e94795aSAndroid Build Coastguard Worker """Loads the given block map file. 229*9e94795aSAndroid Build Coastguard Worker 230*9e94795aSAndroid Build Coastguard Worker Args: 231*9e94795aSAndroid Build Coastguard Worker fn: The filename of the block map file. 232*9e94795aSAndroid Build Coastguard Worker clobbered_blocks: A RangeSet instance for the clobbered blocks. 233*9e94795aSAndroid Build Coastguard Worker allow_shared_blocks: Whether having shared blocks is allowed. 234*9e94795aSAndroid Build Coastguard Worker """ 235*9e94795aSAndroid Build Coastguard Worker remaining = self.care_map 236*9e94795aSAndroid Build Coastguard Worker self.file_map = out = {} 237*9e94795aSAndroid Build Coastguard Worker 238*9e94795aSAndroid Build Coastguard Worker with open(fn) as f: 239*9e94795aSAndroid Build Coastguard Worker for line in f: 240*9e94795aSAndroid Build Coastguard Worker fn, ranges_text = line.rstrip().split(None, 1) 241*9e94795aSAndroid Build Coastguard Worker raw_ranges = rangelib.RangeSet.parse(ranges_text) 242*9e94795aSAndroid Build Coastguard Worker 243*9e94795aSAndroid Build Coastguard Worker # Note: e2fsdroid records holes in the extent tree as "0" blocks. 244*9e94795aSAndroid Build Coastguard Worker # This causes confusion because clobbered_blocks always includes 245*9e94795aSAndroid Build Coastguard Worker # the superblock (physical block #0). Since the 0 blocks here do 246*9e94795aSAndroid Build Coastguard Worker # not represent actual physical blocks, remove them from the set. 247*9e94795aSAndroid Build Coastguard Worker ranges = raw_ranges.subtract(rangelib.RangeSet("0")) 248*9e94795aSAndroid Build Coastguard Worker # b/150334561 we need to perserve the monotonic property of the raw 249*9e94795aSAndroid Build Coastguard Worker # range. Otherwise, the validation script will read the blocks with 250*9e94795aSAndroid Build Coastguard Worker # wrong order when pulling files from the image. 251*9e94795aSAndroid Build Coastguard Worker ranges.monotonic = raw_ranges.monotonic 252*9e94795aSAndroid Build Coastguard Worker ranges.extra['text_str'] = ranges_text 253*9e94795aSAndroid Build Coastguard Worker 254*9e94795aSAndroid Build Coastguard Worker if allow_shared_blocks: 255*9e94795aSAndroid Build Coastguard Worker # Find the shared blocks that have been claimed by others. If so, tag 256*9e94795aSAndroid Build Coastguard Worker # the entry so that we can skip applying imgdiff on this file. 257*9e94795aSAndroid Build Coastguard Worker shared_blocks = ranges.subtract(remaining) 258*9e94795aSAndroid Build Coastguard Worker if shared_blocks: 259*9e94795aSAndroid Build Coastguard Worker non_shared = ranges.subtract(shared_blocks) 260*9e94795aSAndroid Build Coastguard Worker if not non_shared: 261*9e94795aSAndroid Build Coastguard Worker continue 262*9e94795aSAndroid Build Coastguard Worker 263*9e94795aSAndroid Build Coastguard Worker # Put the non-shared RangeSet as the value in the block map, which 264*9e94795aSAndroid Build Coastguard Worker # has a copy of the original RangeSet. 265*9e94795aSAndroid Build Coastguard Worker non_shared.extra['uses_shared_blocks'] = ranges 266*9e94795aSAndroid Build Coastguard Worker ranges = non_shared 267*9e94795aSAndroid Build Coastguard Worker 268*9e94795aSAndroid Build Coastguard Worker out[fn] = ranges 269*9e94795aSAndroid Build Coastguard Worker assert ranges.size() == ranges.intersect(remaining).size() 270*9e94795aSAndroid Build Coastguard Worker 271*9e94795aSAndroid Build Coastguard Worker # Currently we assume that blocks in clobbered_blocks are not part of 272*9e94795aSAndroid Build Coastguard Worker # any file. 273*9e94795aSAndroid Build Coastguard Worker assert not clobbered_blocks.overlaps(ranges) 274*9e94795aSAndroid Build Coastguard Worker remaining = remaining.subtract(ranges) 275*9e94795aSAndroid Build Coastguard Worker 276*9e94795aSAndroid Build Coastguard Worker remaining = remaining.subtract(clobbered_blocks) 277*9e94795aSAndroid Build Coastguard Worker 278*9e94795aSAndroid Build Coastguard Worker # For all the remaining blocks in the care_map (ie, those that 279*9e94795aSAndroid Build Coastguard Worker # aren't part of the data for any file nor part of the clobbered_blocks), 280*9e94795aSAndroid Build Coastguard Worker # divide them into blocks that are all zero and blocks that aren't. 281*9e94795aSAndroid Build Coastguard Worker # (Zero blocks are handled specially because (1) there are usually 282*9e94795aSAndroid Build Coastguard Worker # a lot of them and (2) bsdiff handles files with long sequences of 283*9e94795aSAndroid Build Coastguard Worker # repeated bytes especially poorly.) 284*9e94795aSAndroid Build Coastguard Worker 285*9e94795aSAndroid Build Coastguard Worker zero_blocks = [] 286*9e94795aSAndroid Build Coastguard Worker nonzero_blocks = [] 287*9e94795aSAndroid Build Coastguard Worker reference = '\0' * self.blocksize 288*9e94795aSAndroid Build Coastguard Worker 289*9e94795aSAndroid Build Coastguard Worker # Workaround for bug 23227672. For squashfs, we don't have a system.map. So 290*9e94795aSAndroid Build Coastguard Worker # the whole system image will be treated as a single file. But for some 291*9e94795aSAndroid Build Coastguard Worker # unknown bug, the updater will be killed due to OOM when writing back the 292*9e94795aSAndroid Build Coastguard Worker # patched image to flash (observed on lenok-userdebug MEA49). Prior to 293*9e94795aSAndroid Build Coastguard Worker # getting a real fix, we evenly divide the non-zero blocks into smaller 294*9e94795aSAndroid Build Coastguard Worker # groups (currently 1024 blocks or 4MB per group). 295*9e94795aSAndroid Build Coastguard Worker # Bug: 23227672 296*9e94795aSAndroid Build Coastguard Worker MAX_BLOCKS_PER_GROUP = 1024 297*9e94795aSAndroid Build Coastguard Worker nonzero_groups = [] 298*9e94795aSAndroid Build Coastguard Worker 299*9e94795aSAndroid Build Coastguard Worker f = self.simg_f 300*9e94795aSAndroid Build Coastguard Worker for s, e in remaining: 301*9e94795aSAndroid Build Coastguard Worker for b in range(s, e): 302*9e94795aSAndroid Build Coastguard Worker idx = bisect.bisect_right(self.offset_index, b) - 1 303*9e94795aSAndroid Build Coastguard Worker chunk_start, _, filepos, fill_data = self.offset_map[idx] 304*9e94795aSAndroid Build Coastguard Worker if filepos is not None: 305*9e94795aSAndroid Build Coastguard Worker filepos += (b-chunk_start) * self.blocksize 306*9e94795aSAndroid Build Coastguard Worker f.seek(filepos, os.SEEK_SET) 307*9e94795aSAndroid Build Coastguard Worker data = f.read(self.blocksize) 308*9e94795aSAndroid Build Coastguard Worker else: 309*9e94795aSAndroid Build Coastguard Worker if fill_data == reference[:4]: # fill with all zeros 310*9e94795aSAndroid Build Coastguard Worker data = reference 311*9e94795aSAndroid Build Coastguard Worker else: 312*9e94795aSAndroid Build Coastguard Worker data = None 313*9e94795aSAndroid Build Coastguard Worker 314*9e94795aSAndroid Build Coastguard Worker if data == reference: 315*9e94795aSAndroid Build Coastguard Worker zero_blocks.append(b) 316*9e94795aSAndroid Build Coastguard Worker zero_blocks.append(b+1) 317*9e94795aSAndroid Build Coastguard Worker else: 318*9e94795aSAndroid Build Coastguard Worker nonzero_blocks.append(b) 319*9e94795aSAndroid Build Coastguard Worker nonzero_blocks.append(b+1) 320*9e94795aSAndroid Build Coastguard Worker 321*9e94795aSAndroid Build Coastguard Worker if len(nonzero_blocks) >= MAX_BLOCKS_PER_GROUP: 322*9e94795aSAndroid Build Coastguard Worker nonzero_groups.append(nonzero_blocks) 323*9e94795aSAndroid Build Coastguard Worker # Clear the list. 324*9e94795aSAndroid Build Coastguard Worker nonzero_blocks = [] 325*9e94795aSAndroid Build Coastguard Worker 326*9e94795aSAndroid Build Coastguard Worker if nonzero_blocks: 327*9e94795aSAndroid Build Coastguard Worker nonzero_groups.append(nonzero_blocks) 328*9e94795aSAndroid Build Coastguard Worker nonzero_blocks = [] 329*9e94795aSAndroid Build Coastguard Worker 330*9e94795aSAndroid Build Coastguard Worker assert zero_blocks or nonzero_groups or clobbered_blocks 331*9e94795aSAndroid Build Coastguard Worker 332*9e94795aSAndroid Build Coastguard Worker if zero_blocks: 333*9e94795aSAndroid Build Coastguard Worker out["__ZERO"] = rangelib.RangeSet(data=zero_blocks) 334*9e94795aSAndroid Build Coastguard Worker if nonzero_groups: 335*9e94795aSAndroid Build Coastguard Worker for i, blocks in enumerate(nonzero_groups): 336*9e94795aSAndroid Build Coastguard Worker out["__NONZERO-%d" % i] = rangelib.RangeSet(data=blocks) 337*9e94795aSAndroid Build Coastguard Worker if clobbered_blocks: 338*9e94795aSAndroid Build Coastguard Worker out["__COPY"] = clobbered_blocks 339*9e94795aSAndroid Build Coastguard Worker 340*9e94795aSAndroid Build Coastguard Worker def ResetFileMap(self): 341*9e94795aSAndroid Build Coastguard Worker """Throw away the file map and treat the entire image as 342*9e94795aSAndroid Build Coastguard Worker undifferentiated data.""" 343*9e94795aSAndroid Build Coastguard Worker self.file_map = {"__DATA": self.care_map} 344*9e94795aSAndroid Build Coastguard Worker 345*9e94795aSAndroid Build Coastguard Worker 346*9e94795aSAndroid Build Coastguard Workerdef GetImagePartitionSize(img): 347*9e94795aSAndroid Build Coastguard Worker try: 348*9e94795aSAndroid Build Coastguard Worker simg = SparseImage(img, build_map=False) 349*9e94795aSAndroid Build Coastguard Worker return simg.blocksize * simg.total_blocks 350*9e94795aSAndroid Build Coastguard Worker except ValueError: 351*9e94795aSAndroid Build Coastguard Worker return os.path.getsize(img) 352*9e94795aSAndroid Build Coastguard Worker 353*9e94795aSAndroid Build Coastguard Worker 354*9e94795aSAndroid Build Coastguard Workerif __name__ == '__main__': 355*9e94795aSAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 356*9e94795aSAndroid Build Coastguard Worker parser.add_argument('image') 357*9e94795aSAndroid Build Coastguard Worker parser.add_argument('--get_partition_size', action='store_true', 358*9e94795aSAndroid Build Coastguard Worker help='Return partition size of the image') 359*9e94795aSAndroid Build Coastguard Worker args = parser.parse_args() 360*9e94795aSAndroid Build Coastguard Worker if args.get_partition_size: 361*9e94795aSAndroid Build Coastguard Worker print(GetImagePartitionSize(args.image)) 362