applier.py revision 16416600a92a60294cd57aceec170a13ed72ed19
1553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 2553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# Use of this source code is governed by a BSD-style license that can be 3553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# found in the LICENSE file. 4553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 5553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold"""Applying a Chrome OS update payload. 6553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 7553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad ArnoldThis module is used internally by the main Payload class for applying an update 8553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldpayload. The interface for invoking the applier is as follows: 9553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 10553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold applier = PayloadApplier(payload) 11553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold applier.Run(...) 12553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 13553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold""" 14553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 15553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport array 16553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport bz2 17553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport hashlib 18553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport os 19553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport shutil 20553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport subprocess 21553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport sys 22553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport tempfile 23553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 24553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport common 25553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldfrom error import PayloadError 26553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 27553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 28553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# 29553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# Helper functions. 30553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# 31382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnolddef _VerifySha256(file_obj, expected_hash, name, length=-1): 32553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Verifies the SHA256 hash of a file. 33553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 34553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 35553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold file_obj: file object to read 36553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold expected_hash: the hash digest we expect to be getting 37553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold name: name string of this hash, for error reporting 38382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold length: precise length of data to verify (optional) 39553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 40382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold PayloadError if computed hash doesn't match expected one, or if fails to 41382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold read the specified length of data. 42553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 43553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 44553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # pylint: disable=E1101 45553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold hasher = hashlib.sha256() 46553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold block_length = 1024 * 1024 47382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold max_length = length if length >= 0 else sys.maxint 48553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 49382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold while max_length > 0: 50553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold read_length = min(max_length, block_length) 51553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data = file_obj.read(read_length) 52553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if not data: 53553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold break 54553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold max_length -= len(data) 55553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold hasher.update(data) 56553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 57382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold if length >= 0 and max_length > 0: 58382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold raise PayloadError( 59382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold 'insufficient data (%d instead of %d) when verifying %s' % 60382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold (length - max_length, length, name)) 61382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold 62553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold actual_hash = hasher.digest() 63553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if actual_hash != expected_hash: 64553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('%s hash (%s) not as expected (%s)' % 659640537ac1b6abfae866424a11e6869228fb7cacGilad Arnold (name, common.FormatSha256(actual_hash), 669640537ac1b6abfae866424a11e6869228fb7cacGilad Arnold common.FormatSha256(expected_hash))) 67553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 68553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 69553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnolddef _ReadExtents(file_obj, extents, block_size, max_length=-1): 70553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Reads data from file as defined by extent sequence. 71553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 72553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold This tries to be efficient by not copying data as it is read in chunks. 73553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 74553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 75553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold file_obj: file object 76553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold extents: sequence of block extents (offset and length) 77553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold block_size: size of each block 78553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold max_length: maximum length to read (optional) 79553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Returns: 80553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold A character array containing the concatenated read data. 81553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 82553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 83553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data = array.array('c') 84553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold for ex in extents: 85553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if max_length == 0: 86553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold break 87553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold file_obj.seek(ex.start_block * block_size) 88553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold read_length = ex.num_blocks * block_size 89553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if max_length > 0: 90553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold read_length = min(max_length, read_length) 91553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold max_length -= read_length 92553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data.fromfile(file_obj, read_length) 93553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold return data 94553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 95553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 96553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnolddef _WriteExtents(file_obj, data, extents, block_size, base_name): 97553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Write data to file as defined by extent sequence. 98553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 99553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold This tries to be efficient by not copy data as it is written in chunks. 100553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 101553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 102553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold file_obj: file object 103553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data: data to write 104553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold extents: sequence of block extents (offset and length) 105553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold block_size: size of each block 106553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold base_name: name string of extent block for error reporting 107553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 108553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold PayloadError when things don't add up. 109553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 110553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 111553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_offset = 0 112553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_length = len(data) 113553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold for ex, ex_name in common.ExtentIter(extents, base_name): 114553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if data_offset == data_length: 115553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('%s: more write extents than data' % ex_name) 116553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold write_length = min(data_length - data_offset, ex.num_blocks * block_size) 117553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold file_obj.seek(ex.start_block * block_size) 118553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_view = buffer(data, data_offset, write_length) 119553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold file_obj.write(data_view) 120553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_offset += write_length 121553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 122553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if data_offset < data_length: 123553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('%s: more data than write extents' % base_name) 124553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 125553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 126553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# 127553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# Payload application. 128553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# 129553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldclass PayloadApplier(object): 130553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Applying an update payload. 131553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 132553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold This is a short-lived object whose purpose is to isolate the logic used for 133553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold applying an update payload. 134553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 135553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 136553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 137553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold def __init__(self, payload): 138553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold assert payload.is_init, 'uninitialized update payload' 139553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.payload = payload 140553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.block_size = payload.manifest.block_size 141553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 142553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold def _ApplyReplaceOperation(self, op, op_name, out_data, part_file, part_size): 143553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Applies a REPLACE{,_BZ} operation. 144553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 145553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 146553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold op: the operation object 147553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold op_name: name string for error reporting 148553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold out_data: the data to be written 149553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_file: the partition file object 150553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_size: the size of the partition 151553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 152553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold PayloadError if something goes wrong. 153553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 154553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 155553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold block_size = self.block_size 156553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_length = len(out_data) 157553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 158553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Decompress data if needed. 159553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if op.type == common.OpType.REPLACE_BZ: 160553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold out_data = bz2.decompress(out_data) 161553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_length = len(out_data) 162553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 163553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Write data to blocks specified in dst extents. 164553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_start = 0 165553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold for ex, ex_name in common.ExtentIter(op.dst_extents, 166553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold '%s.dst_extents' % op_name): 167553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold start_block = ex.start_block 168553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold num_blocks = ex.num_blocks 169553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold count = num_blocks * block_size 170553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 171553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Make sure it's not a fake (signature) operation. 172553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if start_block != common.PSEUDO_EXTENT_MARKER: 173553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_end = data_start + count 174553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 175553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Make sure we're not running past partition boundary. 176553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if (start_block + num_blocks) * block_size > part_size: 177553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError( 178553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold '%s: extent (%s) exceeds partition size (%d)' % 179553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold (ex_name, common.FormatExtent(ex, block_size), 180553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_size)) 181553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 182553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Make sure that we have enough data to write. 183553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if data_end >= data_length + block_size: 184553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError( 185553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold '%s: more dst blocks than data (even with padding)') 186553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 187553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Pad with zeros if necessary. 188553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if data_end > data_length: 189553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold padding = data_end - data_length 190553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold out_data += '\0' * padding 191553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 192553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.payload.payload_file.seek(start_block * block_size) 193553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_file.seek(start_block * block_size) 194553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_file.write(out_data[data_start:data_end]) 195553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 196553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_start += count 197553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 198553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Make sure we wrote all data. 199553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if data_start < data_length: 200553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('%s: wrote fewer bytes (%d) than expected (%d)' % 201553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold (op_name, data_start, data_length)) 202553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 203553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold def _ApplyMoveOperation(self, op, op_name, part_file): 204553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Applies a MOVE operation. 205553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 206553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 207553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold op: the operation object 208553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold op_name: name string for error reporting 209553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_file: the partition file object 210553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 211553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold PayloadError if something goes wrong. 212553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 213553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 214553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold block_size = self.block_size 215553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 216553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Gather input raw data from src extents. 217553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold in_data = _ReadExtents(part_file, op.src_extents, block_size) 218553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 219553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Dump extracted data to dst extents. 220553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold _WriteExtents(part_file, in_data, op.dst_extents, block_size, 221553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold '%s.dst_extents' % op_name) 222553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 223553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold def _ApplyBsdiffOperation(self, op, op_name, patch_data, part_file): 224553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Applies a BSDIFF operation. 225553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 226553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 227553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold op: the operation object 228553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold op_name: name string for error reporting 229553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold patch_data: the binary patch content 230553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_file: the partition file object 231553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 232553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold PayloadError if something goes wrong. 233553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 234553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 235553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold block_size = self.block_size 236553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 237553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Gather input raw data and write to a temp file. 238553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold in_data = _ReadExtents(part_file, op.src_extents, block_size, 239553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold max_length=op.src_length) 240553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold with tempfile.NamedTemporaryFile(delete=False) as in_file: 241553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold in_file_name = in_file.name 242553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold in_file.write(in_data) 243553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 244553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Dump patch data to file. 245553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold with tempfile.NamedTemporaryFile(delete=False) as patch_file: 246553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold patch_file_name = patch_file.name 247553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold patch_file.write(patch_data) 248553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 249553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Allocate tepmorary output file. 250553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold with tempfile.NamedTemporaryFile(delete=False) as out_file: 251553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold out_file_name = out_file.name 252553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 253553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Invoke bspatch. 254553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold bspatch_cmd = ['bspatch', in_file_name, out_file_name, patch_file_name] 255553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold subprocess.check_call(bspatch_cmd) 256553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 257553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Read output. 258553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold with open(out_file_name, 'rb') as out_file: 259553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold out_data = out_file.read() 260553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if len(out_data) != op.dst_length: 261553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError( 262553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold '%s: actual patched data length (%d) not as expected (%d)' % 263553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold (op_name, len(out_data), op.dst_length)) 264553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 265553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Write output back to partition, with padding. 266553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold unaligned_out_len = len(out_data) % block_size 267553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if unaligned_out_len: 268553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold out_data += '\0' * (block_size - unaligned_out_len) 269553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold _WriteExtents(part_file, out_data, op.dst_extents, block_size, 270553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold '%s.dst_extents' % op_name) 271553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 272553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Delete all temporary files. 273553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold os.remove(in_file_name) 274553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold os.remove(out_file_name) 275553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold os.remove(patch_file_name) 276553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 277553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold def _ApplyOperations(self, operations, base_name, part_file, part_size): 278553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Applies a sequence of update operations to a partition. 279553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 280553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold This assumes an in-place update semantics, namely all reads are performed 281553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold first, then the data is processed and written back to the same file. 282553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 283553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 284553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold operations: the sequence of operations 285553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold base_name: the name of the operation sequence 286553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_file: the partition file object, open for reading/writing 287553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_size: the partition size 288553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 289553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold PayloadError if anything goes wrong while processing the payload. 290553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 291553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 292553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold for op, op_name in common.OperationIter(operations, base_name): 293553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Read data blob. 294553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data = self.payload.ReadDataBlob(op.data_offset, op.data_length) 295553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 296553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if op.type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ): 297553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self._ApplyReplaceOperation(op, op_name, data, part_file, part_size) 298553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold elif op.type == common.OpType.MOVE: 299553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self._ApplyMoveOperation(op, op_name, part_file) 300553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold elif op.type == common.OpType.BSDIFF: 301553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self._ApplyBsdiffOperation(op, op_name, data, part_file) 302553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold else: 303553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('%s: unknown operation type (%d)' % 304553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold (op_name, op.type)) 305553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 306553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold def _ApplyToPartition(self, operations, part_name, base_name, 30716416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold new_part_file_name, new_part_info, 30816416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold old_part_file_name=None, old_part_info=None): 309553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Applies an update to a partition. 310553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 311553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 312553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold operations: the sequence of update operations to apply 313553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_name: the name of the partition, for error reporting 314553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold base_name: the name of the operation sequence 31516416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold new_part_file_name: file name to write partition data to 31616416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold new_part_info: size and expected hash of dest partition 31716416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold old_part_file_name: file name of source partition (optional) 31816416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold old_part_info: size and expected hash of source partition (optional) 319553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 320553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold PayloadError if anything goes wrong with the update. 321553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 322553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 323553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Do we have a source partition? 32416416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold if old_part_file_name: 325553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Verify the source partition. 32616416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold with open(old_part_file_name, 'rb') as old_part_file: 32716416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold _VerifySha256(old_part_file, old_part_info.hash, part_name, 32816416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold length=old_part_info.size) 329553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 330553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Copy the src partition to the dst one. 33116416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold shutil.copyfile(old_part_file_name, new_part_file_name) 332553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold else: 333553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Preallocate the dst partition file. 334553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold subprocess.check_call( 33516416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold ['fallocate', '-l', str(new_part_info.size), new_part_file_name]) 336553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 337553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Apply operations. 33816416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold with open(new_part_file_name, 'r+b') as new_part_file: 33916416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold self._ApplyOperations(operations, base_name, new_part_file, 34016416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold new_part_info.size) 341553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 342553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Verify the resulting partition. 34316416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold with open(new_part_file_name, 'rb') as new_part_file: 34416416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold _VerifySha256(new_part_file, new_part_info.hash, part_name, 34516416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold length=new_part_info.size) 346553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 34716416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold def Run(self, new_kernel_part, new_rootfs_part, old_kernel_part=None, 34816416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold old_rootfs_part=None): 349553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Applier entry point, invoking all update operations. 350553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 351553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 35216416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold new_kernel_part: name of dest kernel partition file 35316416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold new_rootfs_part: name of dest rootfs partition file 35416416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold old_kernel_part: name of source kernel partition file (optional) 35516416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold old_rootfs_part: name of source rootfs partition file (optional) 356553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 357553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold PayloadError if payload application failed. 358553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 359553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 360553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.payload.ResetFile() 361553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 362553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Make sure the arguments are sane and match the payload. 36316416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold if not (new_kernel_part and new_rootfs_part): 364553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('missing dst {kernel,rootfs} partitions') 365553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 36616416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold if not (old_kernel_part or old_rootfs_part): 367553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if not self.payload.IsFull(): 368553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('trying to apply a non-full update without src ' 369553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold '{kernel,rootfs} partitions') 37016416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold elif old_kernel_part and old_rootfs_part: 371553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if not self.payload.IsDelta(): 372553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('trying to apply a non-delta update onto src ' 373553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold '{kernel,rootfs} partitions') 374553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold else: 375553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('not all src partitions provided') 376553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 377553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Apply update to rootfs. 378553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self._ApplyToPartition( 379553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.payload.manifest.install_operations, 'rootfs', 38016416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold 'install_operations', new_rootfs_part, 38116416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold self.payload.manifest.new_rootfs_info, old_rootfs_part, 382553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.payload.manifest.old_rootfs_info) 383553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 384553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Apply update to kernel update. 385553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self._ApplyToPartition( 386553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.payload.manifest.kernel_install_operations, 'kernel', 38716416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold 'kernel_install_operations', new_kernel_part, 38816416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold self.payload.manifest.new_kernel_info, old_kernel_part, 389553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.payload.manifest.old_kernel_info) 390