applier.py revision 92161a7b83c3a491871a167a4d46e4e6b6101e81
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 1512f59aa1cffec0ee531daccc6de7469870f86302Allie Woodfrom __future__ import print_function 1612f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 17553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport array 18553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport bz2 19553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport hashlib 20658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnoldimport itertools 21553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport os 22553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport shutil 23553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport subprocess 24553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport sys 25553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport tempfile 26553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 27553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldimport common 28553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldfrom error import PayloadError 29553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 30553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 31553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# 32553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# Helper functions. 33553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# 34382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnolddef _VerifySha256(file_obj, expected_hash, name, length=-1): 35553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Verifies the SHA256 hash of a file. 36553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 37553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 38553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold file_obj: file object to read 39553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold expected_hash: the hash digest we expect to be getting 40553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold name: name string of this hash, for error reporting 41382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold length: precise length of data to verify (optional) 4212f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 43553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 44382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold PayloadError if computed hash doesn't match expected one, or if fails to 45382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold read the specified length of data. 46553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 47553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # pylint: disable=E1101 48553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold hasher = hashlib.sha256() 49553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold block_length = 1024 * 1024 50382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold max_length = length if length >= 0 else sys.maxint 51553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 52382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold while max_length > 0: 53553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold read_length = min(max_length, block_length) 54553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data = file_obj.read(read_length) 55553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if not data: 56553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold break 57553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold max_length -= len(data) 58553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold hasher.update(data) 59553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 60382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold if length >= 0 and max_length > 0: 61382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold raise PayloadError( 62382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold 'insufficient data (%d instead of %d) when verifying %s' % 63382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold (length - max_length, length, name)) 64382df5ce2f4b67bf0998b01c6fedcdb5c35ebef9Gilad Arnold 65553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold actual_hash = hasher.digest() 66553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if actual_hash != expected_hash: 67553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('%s hash (%s) not as expected (%s)' % 689640537ac1b6abfae866424a11e6869228fb7cacGilad Arnold (name, common.FormatSha256(actual_hash), 699640537ac1b6abfae866424a11e6869228fb7cacGilad Arnold common.FormatSha256(expected_hash))) 70553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 71553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 72553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnolddef _ReadExtents(file_obj, extents, block_size, max_length=-1): 73553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Reads data from file as defined by extent sequence. 74553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 75553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold This tries to be efficient by not copying data as it is read in chunks. 76553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 77553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 78553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold file_obj: file object 79553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold extents: sequence of block extents (offset and length) 80553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold block_size: size of each block 81553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold max_length: maximum length to read (optional) 8212f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 83553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Returns: 84553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold A character array containing the concatenated read data. 85553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 86553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data = array.array('c') 87272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold if max_length < 0: 88272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold max_length = sys.maxint 89553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold for ex in extents: 90553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if max_length == 0: 91553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold break 92272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold read_length = min(max_length, ex.num_blocks * block_size) 93658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold 94658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold # Fill with zeros or read from file, depending on the type of extent. 95658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold if ex.start_block == common.PSEUDO_EXTENT_MARKER: 96658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold data.extend(itertools.repeat('\0', read_length)) 97658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold else: 98658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold file_obj.seek(ex.start_block * block_size) 99658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold data.fromfile(file_obj, read_length) 100658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold 101272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold max_length -= read_length 102658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold 103553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold return data 104553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 105553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 106553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnolddef _WriteExtents(file_obj, data, extents, block_size, base_name): 107272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold """Writes data to file as defined by extent sequence. 108553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 109553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold This tries to be efficient by not copy data as it is written in chunks. 110553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 111553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 112553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold file_obj: file object 113553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data: data to write 114553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold extents: sequence of block extents (offset and length) 115553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold block_size: size of each block 116272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold base_name: name string of extent sequence for error reporting 11712f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 118553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 119553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold PayloadError when things don't add up. 120553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 121553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_offset = 0 122553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_length = len(data) 123553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold for ex, ex_name in common.ExtentIter(extents, base_name): 124272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold if not data_length: 125553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('%s: more write extents than data' % ex_name) 126272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold write_length = min(data_length, ex.num_blocks * block_size) 127658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold 128658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold # Only do actual writing if this is not a pseudo-extent. 129658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold if ex.start_block != common.PSEUDO_EXTENT_MARKER: 130658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold file_obj.seek(ex.start_block * block_size) 131658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold data_view = buffer(data, data_offset, write_length) 132658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold file_obj.write(data_view) 133658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold 134553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_offset += write_length 135272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold data_length -= write_length 136553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 137272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold if data_length: 138553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('%s: more data than write extents' % base_name) 139553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 140553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 141272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnolddef _ExtentsToBspatchArg(extents, block_size, base_name, data_length=-1): 142272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold """Translates an extent sequence into a bspatch-compatible string argument. 143272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold 144272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold Args: 145272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold extents: sequence of block extents (offset and length) 146272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold block_size: size of each block 147272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold base_name: name string of extent sequence for error reporting 148272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold data_length: the actual total length of the data in bytes (optional) 14912f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 150272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold Returns: 151272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold A tuple consisting of (i) a string of the form 152272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold "off_1:len_1,...,off_n:len_n", (ii) an offset where zero padding is needed 153272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold for filling the last extent, (iii) the length of the padding (zero means no 154272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold padding is needed and the extents cover the full length of data). 15512f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 156272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold Raises: 157272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold PayloadError if data_length is too short or too long. 158272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold """ 159272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold arg = '' 160272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold pad_off = pad_len = 0 161272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold if data_length < 0: 162272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold data_length = sys.maxint 163272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold for ex, ex_name in common.ExtentIter(extents, base_name): 164272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold if not data_length: 165272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold raise PayloadError('%s: more extents than total data length' % ex_name) 166658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold 167658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold is_pseudo = ex.start_block == common.PSEUDO_EXTENT_MARKER 168658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold start_byte = -1 if is_pseudo else ex.start_block * block_size 169272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold num_bytes = ex.num_blocks * block_size 170272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold if data_length < num_bytes: 171658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold # We're only padding a real extent. 172658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold if not is_pseudo: 173658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold pad_off = start_byte + data_length 174658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold pad_len = num_bytes - data_length 175658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold 176272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold num_bytes = data_length 177658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold 178272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold arg += '%s%d:%d' % (arg and ',', start_byte, num_bytes) 179272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold data_length -= num_bytes 180272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold 181272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold if data_length: 182272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold raise PayloadError('%s: extents not covering full data length' % base_name) 183272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold 184272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold return arg, pad_off, pad_len 185272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold 186272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold 187553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# 188553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# Payload application. 189553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold# 190553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnoldclass PayloadApplier(object): 191553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Applying an update payload. 192553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 193553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold This is a short-lived object whose purpose is to isolate the logic used for 194553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold applying an update payload. 195553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 196553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 19721a0250e767dd6fc787252b9cc05657405332774Gilad Arnold def __init__(self, payload, bsdiff_in_place=True, bspatch_path=None, 19892161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang imgpatch_path=None, truncate_to_expected_size=True): 199272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold """Initialize the applier. 200272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold 201272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold Args: 202272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold payload: the payload object to check 203272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold bsdiff_in_place: whether to perform BSDIFF operation in-place (optional) 20421a0250e767dd6fc787252b9cc05657405332774Gilad Arnold bspatch_path: path to the bspatch binary (optional) 20592161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang imgpatch_path: path to the imgpatch binary (optional) 206e5fdf189ce3a4628f02a0bd5e09694bf7b815cdfGilad Arnold truncate_to_expected_size: whether to truncate the resulting partitions 207e5fdf189ce3a4628f02a0bd5e09694bf7b815cdfGilad Arnold to their expected sizes, as specified in the 208e5fdf189ce3a4628f02a0bd5e09694bf7b815cdfGilad Arnold payload (optional) 209272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold """ 210553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold assert payload.is_init, 'uninitialized update payload' 211553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.payload = payload 212553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.block_size = payload.manifest.block_size 21312f59aa1cffec0ee531daccc6de7469870f86302Allie Wood self.minor_version = payload.manifest.minor_version 214272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold self.bsdiff_in_place = bsdiff_in_place 21521a0250e767dd6fc787252b9cc05657405332774Gilad Arnold self.bspatch_path = bspatch_path or 'bspatch' 21692161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang self.imgpatch_path = imgpatch_path or 'imgpatch' 217e5fdf189ce3a4628f02a0bd5e09694bf7b815cdfGilad Arnold self.truncate_to_expected_size = truncate_to_expected_size 218553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 219553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold def _ApplyReplaceOperation(self, op, op_name, out_data, part_file, part_size): 220553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Applies a REPLACE{,_BZ} operation. 221553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 222553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 223553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold op: the operation object 224553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold op_name: name string for error reporting 225553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold out_data: the data to be written 226553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_file: the partition file object 227553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_size: the size of the partition 22812f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 229553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 230553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold PayloadError if something goes wrong. 231553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 232553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold block_size = self.block_size 233553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_length = len(out_data) 234553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 235553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Decompress data if needed. 236553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if op.type == common.OpType.REPLACE_BZ: 237553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold out_data = bz2.decompress(out_data) 238553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_length = len(out_data) 239553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 240553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Write data to blocks specified in dst extents. 241553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_start = 0 242553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold for ex, ex_name in common.ExtentIter(op.dst_extents, 243553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold '%s.dst_extents' % op_name): 244553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold start_block = ex.start_block 245553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold num_blocks = ex.num_blocks 246553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold count = num_blocks * block_size 247553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 248553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Make sure it's not a fake (signature) operation. 249553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if start_block != common.PSEUDO_EXTENT_MARKER: 250553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_end = data_start + count 251553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 252553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Make sure we're not running past partition boundary. 253553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if (start_block + num_blocks) * block_size > part_size: 254553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError( 255553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold '%s: extent (%s) exceeds partition size (%d)' % 256553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold (ex_name, common.FormatExtent(ex, block_size), 257553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_size)) 258553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 259553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Make sure that we have enough data to write. 260553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if data_end >= data_length + block_size: 261553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError( 262553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold '%s: more dst blocks than data (even with padding)') 263553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 264553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Pad with zeros if necessary. 265553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if data_end > data_length: 266553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold padding = data_end - data_length 267553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold out_data += '\0' * padding 268553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 269553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.payload.payload_file.seek(start_block * block_size) 270553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_file.seek(start_block * block_size) 271553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_file.write(out_data[data_start:data_end]) 272553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 273553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data_start += count 274553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 275553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Make sure we wrote all data. 276553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if data_start < data_length: 277553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('%s: wrote fewer bytes (%d) than expected (%d)' % 278553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold (op_name, data_start, data_length)) 279553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 280553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold def _ApplyMoveOperation(self, op, op_name, part_file): 281553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Applies a MOVE operation. 282553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 283658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold Note that this operation must read the whole block data from the input and 284658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold only then dump it, due to our in-place update semantics; otherwise, it 285658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold might clobber data midway through. 286658185a5bd909c0abe8a55523a4e3cab3f9b26c8Gilad Arnold 287553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 288553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold op: the operation object 289553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold op_name: name string for error reporting 290553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_file: the partition file object 29112f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 292553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 293553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold PayloadError if something goes wrong. 294553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 295553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold block_size = self.block_size 296553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 297553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Gather input raw data from src extents. 298553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold in_data = _ReadExtents(part_file, op.src_extents, block_size) 299553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 300553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Dump extracted data to dst extents. 301553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold _WriteExtents(part_file, in_data, op.dst_extents, block_size, 302553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold '%s.dst_extents' % op_name) 303553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 30412f59aa1cffec0ee531daccc6de7469870f86302Allie Wood def _ApplyBsdiffOperation(self, op, op_name, patch_data, new_part_file): 305553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Applies a BSDIFF operation. 306553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 307553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 308553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold op: the operation object 309553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold op_name: name string for error reporting 310553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold patch_data: the binary patch content 31112f59aa1cffec0ee531daccc6de7469870f86302Allie Wood new_part_file: the target partition file object 31212f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 31312f59aa1cffec0ee531daccc6de7469870f86302Allie Wood Raises: 31412f59aa1cffec0ee531daccc6de7469870f86302Allie Wood PayloadError if something goes wrong. 31512f59aa1cffec0ee531daccc6de7469870f86302Allie Wood """ 31612f59aa1cffec0ee531daccc6de7469870f86302Allie Wood # Implemented using a SOURCE_BSDIFF operation with the source and target 31712f59aa1cffec0ee531daccc6de7469870f86302Allie Wood # partition set to the new partition. 31892161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang self._ApplyDiffOperation(op, op_name, patch_data, new_part_file, 31992161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang new_part_file) 32012f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 32112f59aa1cffec0ee531daccc6de7469870f86302Allie Wood def _ApplySourceCopyOperation(self, op, op_name, old_part_file, 32212f59aa1cffec0ee531daccc6de7469870f86302Allie Wood new_part_file): 32312f59aa1cffec0ee531daccc6de7469870f86302Allie Wood """Applies a SOURCE_COPY operation. 32412f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 32512f59aa1cffec0ee531daccc6de7469870f86302Allie Wood Args: 32612f59aa1cffec0ee531daccc6de7469870f86302Allie Wood op: the operation object 32712f59aa1cffec0ee531daccc6de7469870f86302Allie Wood op_name: name string for error reporting 32812f59aa1cffec0ee531daccc6de7469870f86302Allie Wood old_part_file: the old partition file object 32912f59aa1cffec0ee531daccc6de7469870f86302Allie Wood new_part_file: the new partition file object 33012f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 331553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 332553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold PayloadError if something goes wrong. 333553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 33412f59aa1cffec0ee531daccc6de7469870f86302Allie Wood if not old_part_file: 33512f59aa1cffec0ee531daccc6de7469870f86302Allie Wood raise PayloadError( 33612f59aa1cffec0ee531daccc6de7469870f86302Allie Wood '%s: no source partition file provided for operation type (%d)' % 33712f59aa1cffec0ee531daccc6de7469870f86302Allie Wood (op_name, op.type)) 33812f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 33912f59aa1cffec0ee531daccc6de7469870f86302Allie Wood block_size = self.block_size 34012f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 34112f59aa1cffec0ee531daccc6de7469870f86302Allie Wood # Gather input raw data from src extents. 34212f59aa1cffec0ee531daccc6de7469870f86302Allie Wood in_data = _ReadExtents(old_part_file, op.src_extents, block_size) 34312f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 34412f59aa1cffec0ee531daccc6de7469870f86302Allie Wood # Dump extracted data to dst extents. 34512f59aa1cffec0ee531daccc6de7469870f86302Allie Wood _WriteExtents(new_part_file, in_data, op.dst_extents, block_size, 34612f59aa1cffec0ee531daccc6de7469870f86302Allie Wood '%s.dst_extents' % op_name) 34712f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 34892161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang def _ApplyDiffOperation(self, op, op_name, patch_data, old_part_file, 34992161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang new_part_file): 35092161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang """Applies a SOURCE_BSDIFF or IMGDIFF operation. 35112f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 35212f59aa1cffec0ee531daccc6de7469870f86302Allie Wood Args: 35312f59aa1cffec0ee531daccc6de7469870f86302Allie Wood op: the operation object 35412f59aa1cffec0ee531daccc6de7469870f86302Allie Wood op_name: name string for error reporting 35512f59aa1cffec0ee531daccc6de7469870f86302Allie Wood patch_data: the binary patch content 35612f59aa1cffec0ee531daccc6de7469870f86302Allie Wood old_part_file: the source partition file object 35712f59aa1cffec0ee531daccc6de7469870f86302Allie Wood new_part_file: the target partition file object 35812f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 35912f59aa1cffec0ee531daccc6de7469870f86302Allie Wood Raises: 36012f59aa1cffec0ee531daccc6de7469870f86302Allie Wood PayloadError if something goes wrong. 36112f59aa1cffec0ee531daccc6de7469870f86302Allie Wood """ 36212f59aa1cffec0ee531daccc6de7469870f86302Allie Wood if not old_part_file: 36312f59aa1cffec0ee531daccc6de7469870f86302Allie Wood raise PayloadError( 36412f59aa1cffec0ee531daccc6de7469870f86302Allie Wood '%s: no source partition file provided for operation type (%d)' % 36512f59aa1cffec0ee531daccc6de7469870f86302Allie Wood (op_name, op.type)) 36612f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 367553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold block_size = self.block_size 368553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 369553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Dump patch data to file. 370553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold with tempfile.NamedTemporaryFile(delete=False) as patch_file: 371553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold patch_file_name = patch_file.name 372553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold patch_file.write(patch_data) 373553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 37412f59aa1cffec0ee531daccc6de7469870f86302Allie Wood if (hasattr(new_part_file, 'fileno') and 37592161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang ((not old_part_file) or hasattr(old_part_file, 'fileno')) and 37692161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang op.type != common.OpType.IMGDIFF): 377272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold # Construct input and output extents argument for bspatch. 378272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold in_extents_arg, _, _ = _ExtentsToBspatchArg( 379272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold op.src_extents, block_size, '%s.src_extents' % op_name, 380272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold data_length=op.src_length) 381272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold out_extents_arg, pad_off, pad_len = _ExtentsToBspatchArg( 382272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold op.dst_extents, block_size, '%s.dst_extents' % op_name, 383272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold data_length=op.dst_length) 384272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold 38512f59aa1cffec0ee531daccc6de7469870f86302Allie Wood new_file_name = '/dev/fd/%d' % new_part_file.fileno() 38612f59aa1cffec0ee531daccc6de7469870f86302Allie Wood # Diff from source partition. 38712f59aa1cffec0ee531daccc6de7469870f86302Allie Wood old_file_name = '/dev/fd/%d' % old_part_file.fileno() 38812f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 389272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold # Invoke bspatch on partition file with extents args. 39012f59aa1cffec0ee531daccc6de7469870f86302Allie Wood bspatch_cmd = [self.bspatch_path, old_file_name, new_file_name, 39112f59aa1cffec0ee531daccc6de7469870f86302Allie Wood patch_file_name, in_extents_arg, out_extents_arg] 392272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold subprocess.check_call(bspatch_cmd) 393272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold 394272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold # Pad with zeros past the total output length. 395272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold if pad_len: 39612f59aa1cffec0ee531daccc6de7469870f86302Allie Wood new_part_file.seek(pad_off) 39712f59aa1cffec0ee531daccc6de7469870f86302Allie Wood new_part_file.write('\0' * pad_len) 398272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold else: 399272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold # Gather input raw data and write to a temp file. 40012f59aa1cffec0ee531daccc6de7469870f86302Allie Wood input_part_file = old_part_file if old_part_file else new_part_file 40112f59aa1cffec0ee531daccc6de7469870f86302Allie Wood in_data = _ReadExtents(input_part_file, op.src_extents, block_size, 402272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold max_length=op.src_length) 403272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold with tempfile.NamedTemporaryFile(delete=False) as in_file: 404272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold in_file_name = in_file.name 405272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold in_file.write(in_data) 406272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold 40712f59aa1cffec0ee531daccc6de7469870f86302Allie Wood # Allocate temporary output file. 408272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold with tempfile.NamedTemporaryFile(delete=False) as out_file: 409272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold out_file_name = out_file.name 410272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold 411272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold # Invoke bspatch. 41292161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang patch_cmd = [self.bspatch_path, in_file_name, out_file_name, 41392161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang patch_file_name] 41492161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang if op.type == common.OpType.IMGDIFF: 41592161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang patch_cmd[0] = self.imgpatch_path 41692161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang subprocess.check_call(patch_cmd) 417272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold 418272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold # Read output. 419272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold with open(out_file_name, 'rb') as out_file: 420272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold out_data = out_file.read() 421272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold if len(out_data) != op.dst_length: 422272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold raise PayloadError( 423272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold '%s: actual patched data length (%d) not as expected (%d)' % 424272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold (op_name, len(out_data), op.dst_length)) 425272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold 426272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold # Write output back to partition, with padding. 427272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold unaligned_out_len = len(out_data) % block_size 428272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold if unaligned_out_len: 429272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold out_data += '\0' * (block_size - unaligned_out_len) 43012f59aa1cffec0ee531daccc6de7469870f86302Allie Wood _WriteExtents(new_part_file, out_data, op.dst_extents, block_size, 431272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold '%s.dst_extents' % op_name) 432272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold 433272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold # Delete input/output files. 434272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold os.remove(in_file_name) 435272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold os.remove(out_file_name) 436553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 437272a499e2db9d72a64490ca5ccbebe8155fc2966Gilad Arnold # Delete patch file. 438553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold os.remove(patch_file_name) 439553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 44012f59aa1cffec0ee531daccc6de7469870f86302Allie Wood def _ApplyOperations(self, operations, base_name, old_part_file, 44112f59aa1cffec0ee531daccc6de7469870f86302Allie Wood new_part_file, part_size): 442553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Applies a sequence of update operations to a partition. 443553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 44412f59aa1cffec0ee531daccc6de7469870f86302Allie Wood This assumes an in-place update semantics for MOVE and BSDIFF, namely all 44512f59aa1cffec0ee531daccc6de7469870f86302Allie Wood reads are performed first, then the data is processed and written back to 44612f59aa1cffec0ee531daccc6de7469870f86302Allie Wood the same file. 447553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 448553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 449553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold operations: the sequence of operations 450553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold base_name: the name of the operation sequence 45112f59aa1cffec0ee531daccc6de7469870f86302Allie Wood old_part_file: the old partition file object, open for reading/writing 45212f59aa1cffec0ee531daccc6de7469870f86302Allie Wood new_part_file: the new partition file object, open for reading/writing 453553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_size: the partition size 45412f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 455553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 456553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold PayloadError if anything goes wrong while processing the payload. 457553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 458553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold for op, op_name in common.OperationIter(operations, base_name): 459553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Read data blob. 460553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold data = self.payload.ReadDataBlob(op.data_offset, op.data_length) 461553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 462553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if op.type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ): 46312f59aa1cffec0ee531daccc6de7469870f86302Allie Wood self._ApplyReplaceOperation(op, op_name, data, new_part_file, part_size) 464553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold elif op.type == common.OpType.MOVE: 46512f59aa1cffec0ee531daccc6de7469870f86302Allie Wood self._ApplyMoveOperation(op, op_name, new_part_file) 466553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold elif op.type == common.OpType.BSDIFF: 46712f59aa1cffec0ee531daccc6de7469870f86302Allie Wood self._ApplyBsdiffOperation(op, op_name, data, new_part_file) 46812f59aa1cffec0ee531daccc6de7469870f86302Allie Wood elif op.type == common.OpType.SOURCE_COPY: 46912f59aa1cffec0ee531daccc6de7469870f86302Allie Wood self._ApplySourceCopyOperation(op, op_name, old_part_file, 47012f59aa1cffec0ee531daccc6de7469870f86302Allie Wood new_part_file) 47192161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang elif op.type in (common.OpType.SOURCE_BSDIFF, common.OpType.IMGDIFF): 47292161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang self._ApplyDiffOperation(op, op_name, data, old_part_file, 47392161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang new_part_file) 474553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold else: 475553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('%s: unknown operation type (%d)' % 476553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold (op_name, op.type)) 477553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 478553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold def _ApplyToPartition(self, operations, part_name, base_name, 47916416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold new_part_file_name, new_part_info, 48016416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold old_part_file_name=None, old_part_info=None): 481553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Applies an update to a partition. 482553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 483553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 484553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold operations: the sequence of update operations to apply 485553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold part_name: the name of the partition, for error reporting 486553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold base_name: the name of the operation sequence 48716416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold new_part_file_name: file name to write partition data to 48816416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold new_part_info: size and expected hash of dest partition 48916416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold old_part_file_name: file name of source partition (optional) 49016416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold old_part_info: size and expected hash of source partition (optional) 49112f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 492553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 493553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold PayloadError if anything goes wrong with the update. 494553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 495553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Do we have a source partition? 49616416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold if old_part_file_name: 497553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Verify the source partition. 49816416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold with open(old_part_file_name, 'rb') as old_part_file: 4994b8f4c2f76dcbac32dc7729458f846bd6eed5c63Gilad Arnold _VerifySha256(old_part_file, old_part_info.hash, 5004b8f4c2f76dcbac32dc7729458f846bd6eed5c63Gilad Arnold 'old ' + part_name, length=old_part_info.size) 501f69065c07bb2eccc88499ba34429f62f707e19b8Gilad Arnold new_part_file_mode = 'r+b' 50212f59aa1cffec0ee531daccc6de7469870f86302Allie Wood if self.minor_version == common.INPLACE_MINOR_PAYLOAD_VERSION: 50312f59aa1cffec0ee531daccc6de7469870f86302Allie Wood # Copy the src partition to the dst one; make sure we don't truncate it. 50412f59aa1cffec0ee531daccc6de7469870f86302Allie Wood shutil.copyfile(old_part_file_name, new_part_file_name) 505d6122bb9df5b593a3ab1fc35fab7f1a0caa53928Sen Jiang elif (self.minor_version == common.SOURCE_MINOR_PAYLOAD_VERSION or 50692161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang self.minor_version == common.OPSRCHASH_MINOR_PAYLOAD_VERSION or 50792161a7b83c3a491871a167a4d46e4e6b6101e81Sen Jiang self.minor_version == common.IMGDIFF_MINOR_PAYLOAD_VERSION): 508d6122bb9df5b593a3ab1fc35fab7f1a0caa53928Sen Jiang # In minor version >= 2, we don't want to copy the partitions, so 509d6122bb9df5b593a3ab1fc35fab7f1a0caa53928Sen Jiang # instead just make the new partition file. 51012f59aa1cffec0ee531daccc6de7469870f86302Allie Wood open(new_part_file_name, 'w').close() 51112f59aa1cffec0ee531daccc6de7469870f86302Allie Wood else: 51212f59aa1cffec0ee531daccc6de7469870f86302Allie Wood raise PayloadError("Unknown minor version: %d" % self.minor_version) 513553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold else: 514f69065c07bb2eccc88499ba34429f62f707e19b8Gilad Arnold # We need to create/truncate the dst partition file. 515f69065c07bb2eccc88499ba34429f62f707e19b8Gilad Arnold new_part_file_mode = 'w+b' 516553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 517553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Apply operations. 518f69065c07bb2eccc88499ba34429f62f707e19b8Gilad Arnold with open(new_part_file_name, new_part_file_mode) as new_part_file: 51912f59aa1cffec0ee531daccc6de7469870f86302Allie Wood old_part_file = (open(old_part_file_name, 'r+b') 52012f59aa1cffec0ee531daccc6de7469870f86302Allie Wood if old_part_file_name else None) 52112f59aa1cffec0ee531daccc6de7469870f86302Allie Wood try: 52212f59aa1cffec0ee531daccc6de7469870f86302Allie Wood self._ApplyOperations(operations, base_name, old_part_file, 52312f59aa1cffec0ee531daccc6de7469870f86302Allie Wood new_part_file, new_part_info.size) 52412f59aa1cffec0ee531daccc6de7469870f86302Allie Wood finally: 52512f59aa1cffec0ee531daccc6de7469870f86302Allie Wood if old_part_file: 52612f59aa1cffec0ee531daccc6de7469870f86302Allie Wood old_part_file.close() 52712f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 528e5fdf189ce3a4628f02a0bd5e09694bf7b815cdfGilad Arnold # Truncate the result, if so instructed. 529e5fdf189ce3a4628f02a0bd5e09694bf7b815cdfGilad Arnold if self.truncate_to_expected_size: 530e5fdf189ce3a4628f02a0bd5e09694bf7b815cdfGilad Arnold new_part_file.seek(0, 2) 531e5fdf189ce3a4628f02a0bd5e09694bf7b815cdfGilad Arnold if new_part_file.tell() > new_part_info.size: 532e5fdf189ce3a4628f02a0bd5e09694bf7b815cdfGilad Arnold new_part_file.seek(new_part_info.size) 533e5fdf189ce3a4628f02a0bd5e09694bf7b815cdfGilad Arnold new_part_file.truncate() 534553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 535553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Verify the resulting partition. 53616416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold with open(new_part_file_name, 'rb') as new_part_file: 5374b8f4c2f76dcbac32dc7729458f846bd6eed5c63Gilad Arnold _VerifySha256(new_part_file, new_part_info.hash, 5384b8f4c2f76dcbac32dc7729458f846bd6eed5c63Gilad Arnold 'new ' + part_name, length=new_part_info.size) 539553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 54016416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold def Run(self, new_kernel_part, new_rootfs_part, old_kernel_part=None, 54116416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold old_rootfs_part=None): 542553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """Applier entry point, invoking all update operations. 543553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 544553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Args: 54516416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold new_kernel_part: name of dest kernel partition file 54616416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold new_rootfs_part: name of dest rootfs partition file 54716416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold old_kernel_part: name of source kernel partition file (optional) 54816416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold old_rootfs_part: name of source rootfs partition file (optional) 54912f59aa1cffec0ee531daccc6de7469870f86302Allie Wood 550553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold Raises: 551553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold PayloadError if payload application failed. 552553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold """ 553553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.payload.ResetFile() 554553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 555553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Make sure the arguments are sane and match the payload. 55616416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold if not (new_kernel_part and new_rootfs_part): 557553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('missing dst {kernel,rootfs} partitions') 558553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 55916416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold if not (old_kernel_part or old_rootfs_part): 560553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if not self.payload.IsFull(): 561553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('trying to apply a non-full update without src ' 562553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold '{kernel,rootfs} partitions') 56316416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold elif old_kernel_part and old_rootfs_part: 564553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold if not self.payload.IsDelta(): 565553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('trying to apply a non-delta update onto src ' 566553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold '{kernel,rootfs} partitions') 567553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold else: 568553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold raise PayloadError('not all src partitions provided') 569553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 570553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Apply update to rootfs. 571553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self._ApplyToPartition( 572553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.payload.manifest.install_operations, 'rootfs', 57316416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold 'install_operations', new_rootfs_part, 57416416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold self.payload.manifest.new_rootfs_info, old_rootfs_part, 575553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.payload.manifest.old_rootfs_info) 576553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold 577553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold # Apply update to kernel update. 578553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self._ApplyToPartition( 579553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.payload.manifest.kernel_install_operations, 'kernel', 58016416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold 'kernel_install_operations', new_kernel_part, 58116416600a92a60294cd57aceec170a13ed72ed19Gilad Arnold self.payload.manifest.new_kernel_info, old_kernel_part, 582553b0ec49bc64fc4b7df4358cd31396a87276d2bGilad Arnold self.payload.manifest.old_kernel_info) 583