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