blob: 5428f66e3457d808b25b14f6a97e2f13cb215fe0 [file] [log] [blame]
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Verifying the integrity of a Chrome OS update payload.
6
7This module is used internally by the main Payload class for verifying the
8integrity of an update payload. The interface for invoking the checks is as
9follows:
10
11 checker = PayloadChecker(payload)
12 checker.Run(...)
Gilad Arnold553b0ec2013-01-26 01:00:39 -080013"""
14
Gilad Arnoldf583a7d2015-02-05 13:23:55 -080015from __future__ import print_function
16
Gilad Arnold553b0ec2013-01-26 01:00:39 -080017import array
18import base64
19import hashlib
Gilad Arnoldcb638912013-06-24 04:57:11 -070020import itertools
Gilad Arnold9b90c932013-05-22 17:12:56 -070021import os
Gilad Arnold553b0ec2013-01-26 01:00:39 -080022import subprocess
23
24import common
Gilad Arnoldcb638912013-06-24 04:57:11 -070025import error
Gilad Arnold553b0ec2013-01-26 01:00:39 -080026import format_utils
27import histogram
28import update_metadata_pb2
29
30
31#
Gilad Arnold9b90c932013-05-22 17:12:56 -070032# Constants.
Gilad Arnold553b0ec2013-01-26 01:00:39 -080033#
Gilad Arnoldcb638912013-06-24 04:57:11 -070034
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070035_CHECK_DST_PSEUDO_EXTENTS = 'dst-pseudo-extents'
36_CHECK_MOVE_SAME_SRC_DST_BLOCK = 'move-same-src-dst-block'
37_CHECK_PAYLOAD_SIG = 'payload-sig'
38CHECKS_TO_DISABLE = (
Gilad Arnold382df5c2013-05-03 12:49:28 -070039 _CHECK_DST_PSEUDO_EXTENTS,
40 _CHECK_MOVE_SAME_SRC_DST_BLOCK,
41 _CHECK_PAYLOAD_SIG,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -070042)
43
Gilad Arnold553b0ec2013-01-26 01:00:39 -080044_TYPE_FULL = 'full'
45_TYPE_DELTA = 'delta'
46
47_DEFAULT_BLOCK_SIZE = 4096
48
Gilad Arnold9b90c932013-05-22 17:12:56 -070049_DEFAULT_PUBKEY_BASE_NAME = 'update-payload-key.pub.pem'
50_DEFAULT_PUBKEY_FILE_NAME = os.path.join(os.path.dirname(__file__),
51 _DEFAULT_PUBKEY_BASE_NAME)
52
Gilad Arnold0d575cd2015-07-13 17:29:21 -070053# Supported minor version map to payload types allowed to be using them.
54_SUPPORTED_MINOR_VERSIONS = {
55 0: (_TYPE_FULL,),
56 1: (_TYPE_DELTA,),
57 2: (_TYPE_DELTA,),
58}
Gilad Arnold553b0ec2013-01-26 01:00:39 -080059
60#
61# Helper functions.
62#
Gilad Arnoldcb638912013-06-24 04:57:11 -070063
Gilad Arnold553b0ec2013-01-26 01:00:39 -080064def _IsPowerOfTwo(val):
65 """Returns True iff val is a power of two."""
66 return val > 0 and (val & (val - 1)) == 0
67
68
69def _AddFormat(format_func, value):
70 """Adds a custom formatted representation to ordinary string representation.
71
72 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -070073 format_func: A value formatter.
74 value: Value to be formatted and returned.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -080075
Gilad Arnold553b0ec2013-01-26 01:00:39 -080076 Returns:
77 A string 'x (y)' where x = str(value) and y = format_func(value).
Gilad Arnold553b0ec2013-01-26 01:00:39 -080078 """
Gilad Arnold6a3a3872013-10-04 18:18:45 -070079 ret = str(value)
80 formatted_str = format_func(value)
81 if formatted_str:
82 ret += ' (%s)' % formatted_str
83 return ret
Gilad Arnold553b0ec2013-01-26 01:00:39 -080084
85
86def _AddHumanReadableSize(size):
87 """Adds a human readable representation to a byte size value."""
88 return _AddFormat(format_utils.BytesToHumanReadable, size)
89
90
91#
92# Payload report generator.
93#
Gilad Arnoldcb638912013-06-24 04:57:11 -070094
Gilad Arnold553b0ec2013-01-26 01:00:39 -080095class _PayloadReport(object):
96 """A payload report generator.
97
98 A report is essentially a sequence of nodes, which represent data points. It
99 is initialized to have a "global", untitled section. A node may be a
100 sub-report itself.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800101 """
102
Gilad Arnoldcb638912013-06-24 04:57:11 -0700103 # Report nodes: Field, sub-report, section.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800104 class Node(object):
105 """A report node interface."""
106
107 @staticmethod
108 def _Indent(indent, line):
109 """Indents a line by a given indentation amount.
110
111 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700112 indent: The indentation amount.
113 line: The line content (string).
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800114
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800115 Returns:
116 The properly indented line (string).
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800117 """
118 return '%*s%s' % (indent, '', line)
119
120 def GenerateLines(self, base_indent, sub_indent, curr_section):
121 """Generates the report lines for this node.
122
123 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700124 base_indent: Base indentation for each line.
125 sub_indent: Additional indentation for sub-nodes.
126 curr_section: The current report section object.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800127
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800128 Returns:
129 A pair consisting of a list of properly indented report lines and a new
130 current section object.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800131 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700132 raise NotImplementedError
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800133
134 class FieldNode(Node):
135 """A field report node, representing a (name, value) pair."""
136
137 def __init__(self, name, value, linebreak, indent):
138 super(_PayloadReport.FieldNode, self).__init__()
139 self.name = name
140 self.value = value
141 self.linebreak = linebreak
142 self.indent = indent
143
144 def GenerateLines(self, base_indent, sub_indent, curr_section):
145 """Generates a properly formatted 'name : value' entry."""
146 report_output = ''
147 if self.name:
148 report_output += self.name.ljust(curr_section.max_field_name_len) + ' :'
149 value_lines = str(self.value).splitlines()
150 if self.linebreak and self.name:
151 report_output += '\n' + '\n'.join(
152 ['%*s%s' % (self.indent, '', line) for line in value_lines])
153 else:
154 if self.name:
155 report_output += ' '
156 report_output += '%*s' % (self.indent, '')
157 cont_line_indent = len(report_output)
158 indented_value_lines = [value_lines[0]]
159 indented_value_lines.extend(['%*s%s' % (cont_line_indent, '', line)
160 for line in value_lines[1:]])
161 report_output += '\n'.join(indented_value_lines)
162
163 report_lines = [self._Indent(base_indent, line + '\n')
164 for line in report_output.split('\n')]
165 return report_lines, curr_section
166
167 class SubReportNode(Node):
168 """A sub-report node, representing a nested report."""
169
170 def __init__(self, title, report):
171 super(_PayloadReport.SubReportNode, self).__init__()
172 self.title = title
173 self.report = report
174
175 def GenerateLines(self, base_indent, sub_indent, curr_section):
176 """Recurse with indentation."""
177 report_lines = [self._Indent(base_indent, self.title + ' =>\n')]
178 report_lines.extend(self.report.GenerateLines(base_indent + sub_indent,
179 sub_indent))
180 return report_lines, curr_section
181
182 class SectionNode(Node):
183 """A section header node."""
184
185 def __init__(self, title=None):
186 super(_PayloadReport.SectionNode, self).__init__()
187 self.title = title
188 self.max_field_name_len = 0
189
190 def GenerateLines(self, base_indent, sub_indent, curr_section):
191 """Dump a title line, return self as the (new) current section."""
192 report_lines = []
193 if self.title:
194 report_lines.append(self._Indent(base_indent,
195 '=== %s ===\n' % self.title))
196 return report_lines, self
197
198 def __init__(self):
199 self.report = []
200 self.last_section = self.global_section = self.SectionNode()
201 self.is_finalized = False
202
203 def GenerateLines(self, base_indent, sub_indent):
204 """Generates the lines in the report, properly indented.
205
206 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700207 base_indent: The indentation used for root-level report lines.
208 sub_indent: The indentation offset used for sub-reports.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800209
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800210 Returns:
211 A list of indented report lines.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800212 """
213 report_lines = []
214 curr_section = self.global_section
215 for node in self.report:
216 node_report_lines, curr_section = node.GenerateLines(
217 base_indent, sub_indent, curr_section)
218 report_lines.extend(node_report_lines)
219
220 return report_lines
221
222 def Dump(self, out_file, base_indent=0, sub_indent=2):
223 """Dumps the report to a file.
224
225 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700226 out_file: File object to output the content to.
227 base_indent: Base indentation for report lines.
228 sub_indent: Added indentation for sub-reports.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800229 """
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800230 report_lines = self.GenerateLines(base_indent, sub_indent)
231 if report_lines and not self.is_finalized:
232 report_lines.append('(incomplete report)\n')
233
234 for line in report_lines:
235 out_file.write(line)
236
237 def AddField(self, name, value, linebreak=False, indent=0):
238 """Adds a field/value pair to the payload report.
239
240 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700241 name: The field's name.
242 value: The field's value.
243 linebreak: Whether the value should be printed on a new line.
244 indent: Amount of extra indent for each line of the value.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800245 """
246 assert not self.is_finalized
247 if name and self.last_section.max_field_name_len < len(name):
248 self.last_section.max_field_name_len = len(name)
249 self.report.append(self.FieldNode(name, value, linebreak, indent))
250
251 def AddSubReport(self, title):
252 """Adds and returns a sub-report with a title."""
253 assert not self.is_finalized
254 sub_report = self.SubReportNode(title, type(self)())
255 self.report.append(sub_report)
256 return sub_report.report
257
258 def AddSection(self, title):
259 """Adds a new section title."""
260 assert not self.is_finalized
261 self.last_section = self.SectionNode(title)
262 self.report.append(self.last_section)
263
264 def Finalize(self):
265 """Seals the report, marking it as complete."""
266 self.is_finalized = True
267
268
269#
270# Payload verification.
271#
Gilad Arnoldcb638912013-06-24 04:57:11 -0700272
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800273class PayloadChecker(object):
274 """Checking the integrity of an update payload.
275
276 This is a short-lived object whose purpose is to isolate the logic used for
277 verifying the integrity of an update payload.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800278 """
279
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700280 def __init__(self, payload, assert_type=None, block_size=0,
281 allow_unhashed=False, disabled_tests=()):
Gilad Arnold272a4992013-05-08 13:12:53 -0700282 """Initialize the checker.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700283
284 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700285 payload: The payload object to check.
286 assert_type: Assert that payload is either 'full' or 'delta' (optional).
287 block_size: Expected filesystem / payload block size (optional).
288 allow_unhashed: Allow operations with unhashed data blobs.
289 disabled_tests: Sequence of tests to disable.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700290 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700291 if not payload.is_init:
292 raise ValueError('Uninitialized update payload.')
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700293
294 # Set checker configuration.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800295 self.payload = payload
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700296 self.block_size = block_size if block_size else _DEFAULT_BLOCK_SIZE
297 if not _IsPowerOfTwo(self.block_size):
Gilad Arnoldcb638912013-06-24 04:57:11 -0700298 raise error.PayloadError(
299 'Expected block (%d) size is not a power of two.' % self.block_size)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700300 if assert_type not in (None, _TYPE_FULL, _TYPE_DELTA):
Gilad Arnoldcb638912013-06-24 04:57:11 -0700301 raise error.PayloadError('Invalid assert_type value (%r).' %
302 assert_type)
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700303 self.payload_type = assert_type
304 self.allow_unhashed = allow_unhashed
305
306 # Disable specific tests.
307 self.check_dst_pseudo_extents = (
308 _CHECK_DST_PSEUDO_EXTENTS not in disabled_tests)
309 self.check_move_same_src_dst_block = (
310 _CHECK_MOVE_SAME_SRC_DST_BLOCK not in disabled_tests)
311 self.check_payload_sig = _CHECK_PAYLOAD_SIG not in disabled_tests
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800312
313 # Reset state; these will be assigned when the manifest is checked.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800314 self.sigs_offset = 0
315 self.sigs_size = 0
Gilad Arnold382df5c2013-05-03 12:49:28 -0700316 self.old_rootfs_fs_size = 0
317 self.old_kernel_fs_size = 0
318 self.new_rootfs_fs_size = 0
319 self.new_kernel_fs_size = 0
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700320 self.minor_version = None
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800321
322 @staticmethod
323 def _CheckElem(msg, name, report, is_mandatory, is_submsg, convert=str,
324 msg_name=None, linebreak=False, indent=0):
325 """Adds an element from a protobuf message to the payload report.
326
327 Checks to see whether a message contains a given element, and if so adds
328 the element value to the provided report. A missing mandatory element
329 causes an exception to be raised.
330
331 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700332 msg: The message containing the element.
333 name: The name of the element.
334 report: A report object to add the element name/value to.
335 is_mandatory: Whether or not this element must be present.
336 is_submsg: Whether this element is itself a message.
337 convert: A function for converting the element value for reporting.
338 msg_name: The name of the message object (for error reporting).
339 linebreak: Whether the value report should induce a line break.
340 indent: Amount of indent used for reporting the value.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800341
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800342 Returns:
343 A pair consisting of the element value and the generated sub-report for
344 it (if the element is a sub-message, None otherwise). If the element is
345 missing, returns (None, None).
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800346
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800347 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700348 error.PayloadError if a mandatory element is missing.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800349 """
350 if not msg.HasField(name):
351 if is_mandatory:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700352 raise error.PayloadError('%smissing mandatory %s %r.' %
353 (msg_name + ' ' if msg_name else '',
354 'sub-message' if is_submsg else 'field',
355 name))
356 return None, None
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800357
358 value = getattr(msg, name)
359 if is_submsg:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700360 return value, report and report.AddSubReport(name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800361 else:
362 if report:
363 report.AddField(name, convert(value), linebreak=linebreak,
364 indent=indent)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700365 return value, None
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800366
367 @staticmethod
368 def _CheckMandatoryField(msg, field_name, report, msg_name, convert=str,
369 linebreak=False, indent=0):
370 """Adds a mandatory field; returning first component from _CheckElem."""
371 return PayloadChecker._CheckElem(msg, field_name, report, True, False,
372 convert=convert, msg_name=msg_name,
373 linebreak=linebreak, indent=indent)[0]
374
375 @staticmethod
376 def _CheckOptionalField(msg, field_name, report, convert=str,
377 linebreak=False, indent=0):
378 """Adds an optional field; returning first component from _CheckElem."""
379 return PayloadChecker._CheckElem(msg, field_name, report, False, False,
380 convert=convert, linebreak=linebreak,
381 indent=indent)[0]
382
383 @staticmethod
384 def _CheckMandatorySubMsg(msg, submsg_name, report, msg_name):
385 """Adds a mandatory sub-message; wrapper for _CheckElem."""
386 return PayloadChecker._CheckElem(msg, submsg_name, report, True, True,
387 msg_name)
388
389 @staticmethod
390 def _CheckOptionalSubMsg(msg, submsg_name, report):
391 """Adds an optional sub-message; wrapper for _CheckElem."""
392 return PayloadChecker._CheckElem(msg, submsg_name, report, False, True)
393
394 @staticmethod
395 def _CheckPresentIff(val1, val2, name1, name2, obj_name):
396 """Checks that val1 is None iff val2 is None.
397
398 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700399 val1: first value to be compared.
400 val2: second value to be compared.
401 name1: name of object holding the first value.
402 name2: name of object holding the second value.
403 obj_name: Name of the object containing these values.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800404
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800405 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700406 error.PayloadError if assertion does not hold.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800407 """
408 if None in (val1, val2) and val1 is not val2:
409 present, missing = (name1, name2) if val2 is None else (name2, name1)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700410 raise error.PayloadError('%r present without %r%s.' %
411 (present, missing,
412 ' in ' + obj_name if obj_name else ''))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800413
414 @staticmethod
415 def _Run(cmd, send_data=None):
416 """Runs a subprocess, returns its output.
417
418 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700419 cmd: Sequence of command-line argument for invoking the subprocess.
420 send_data: Data to feed to the process via its stdin.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800421
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800422 Returns:
423 A tuple containing the stdout and stderr output of the process.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800424 """
425 run_process = subprocess.Popen(cmd, stdin=subprocess.PIPE,
426 stdout=subprocess.PIPE)
Gilad Arnoldcb638912013-06-24 04:57:11 -0700427 try:
428 result = run_process.communicate(input=send_data)
429 finally:
430 exit_code = run_process.wait()
431
432 if exit_code:
433 raise RuntimeError('Subprocess %r failed with code %r.' %
434 (cmd, exit_code))
435
436 return result
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800437
438 @staticmethod
439 def _CheckSha256Signature(sig_data, pubkey_file_name, actual_hash, sig_name):
440 """Verifies an actual hash against a signed one.
441
442 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700443 sig_data: The raw signature data.
444 pubkey_file_name: Public key used for verifying signature.
445 actual_hash: The actual hash digest.
446 sig_name: Signature name for error reporting.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800447
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800448 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700449 error.PayloadError if signature could not be verified.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800450 """
451 if len(sig_data) != 256:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700452 raise error.PayloadError(
453 '%s: signature size (%d) not as expected (256).' %
454 (sig_name, len(sig_data)))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800455 signed_data, _ = PayloadChecker._Run(
456 ['openssl', 'rsautl', '-verify', '-pubin', '-inkey', pubkey_file_name],
457 send_data=sig_data)
458
Gilad Arnold5502b562013-03-08 13:22:31 -0800459 if len(signed_data) != len(common.SIG_ASN1_HEADER) + 32:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700460 raise error.PayloadError('%s: unexpected signed data length (%d).' %
461 (sig_name, len(signed_data)))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800462
Gilad Arnold5502b562013-03-08 13:22:31 -0800463 if not signed_data.startswith(common.SIG_ASN1_HEADER):
Gilad Arnoldcb638912013-06-24 04:57:11 -0700464 raise error.PayloadError('%s: not containing standard ASN.1 prefix.' %
465 sig_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800466
Gilad Arnold5502b562013-03-08 13:22:31 -0800467 signed_hash = signed_data[len(common.SIG_ASN1_HEADER):]
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800468 if signed_hash != actual_hash:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700469 raise error.PayloadError(
470 '%s: signed hash (%s) different from actual (%s).' %
471 (sig_name, common.FormatSha256(signed_hash),
472 common.FormatSha256(actual_hash)))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800473
474 @staticmethod
475 def _CheckBlocksFitLength(length, num_blocks, block_size, length_name,
476 block_name=None):
477 """Checks that a given length fits given block space.
478
479 This ensures that the number of blocks allocated is appropriate for the
480 length of the data residing in these blocks.
481
482 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700483 length: The actual length of the data.
484 num_blocks: The number of blocks allocated for it.
485 block_size: The size of each block in bytes.
486 length_name: Name of length (used for error reporting).
487 block_name: Name of block (used for error reporting).
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800488
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800489 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700490 error.PayloadError if the aforementioned invariant is not satisfied.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800491 """
492 # Check: length <= num_blocks * block_size.
Gilad Arnold382df5c2013-05-03 12:49:28 -0700493 if length > num_blocks * block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700494 raise error.PayloadError(
495 '%s (%d) > num %sblocks (%d) * block_size (%d).' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800496 (length_name, length, block_name or '', num_blocks, block_size))
497
498 # Check: length > (num_blocks - 1) * block_size.
Gilad Arnold382df5c2013-05-03 12:49:28 -0700499 if length <= (num_blocks - 1) * block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700500 raise error.PayloadError(
501 '%s (%d) <= (num %sblocks - 1 (%d)) * block_size (%d).' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800502 (length_name, length, block_name or '', num_blocks - 1, block_size))
503
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700504 def _CheckManifestMinorVersion(self, report):
505 """Checks the payload manifest minor_version field.
Allie Wood7cf9f132015-02-26 14:28:19 -0800506
507 Args:
508 report: The report object to add to.
Allie Wood7cf9f132015-02-26 14:28:19 -0800509
510 Raises:
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700511 error.PayloadError if any of the checks fail.
Allie Wood7cf9f132015-02-26 14:28:19 -0800512 """
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700513 self.minor_version = self._CheckOptionalField(self.payload.manifest,
514 'minor_version', report)
515 if self.minor_version in _SUPPORTED_MINOR_VERSIONS:
516 if self.payload_type not in _SUPPORTED_MINOR_VERSIONS[self.minor_version]:
Allie Wood7cf9f132015-02-26 14:28:19 -0800517 raise error.PayloadError(
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700518 'Minor version %d not compatible with payload type %s.' %
519 (self.minor_version, self.payload_type))
520 elif self.minor_version is None:
521 raise error.PayloadError('Minor version is not set.')
Allie Wood7cf9f132015-02-26 14:28:19 -0800522 else:
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700523 raise error.PayloadError('Unsupported minor version: %d' %
524 self.minor_version)
Allie Wood7cf9f132015-02-26 14:28:19 -0800525
Gilad Arnold382df5c2013-05-03 12:49:28 -0700526 def _CheckManifest(self, report, rootfs_part_size=0, kernel_part_size=0):
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800527 """Checks the payload manifest.
528
529 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700530 report: A report object to add to.
531 rootfs_part_size: Size of the rootfs partition in bytes.
532 kernel_part_size: Size of the kernel partition in bytes.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800533
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800534 Returns:
535 A tuple consisting of the partition block size used during the update
536 (integer), the signatures block offset and size.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800537
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800538 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700539 error.PayloadError if any of the checks fail.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800540 """
541 manifest = self.payload.manifest
542 report.AddSection('manifest')
543
544 # Check: block_size must exist and match the expected value.
545 actual_block_size = self._CheckMandatoryField(manifest, 'block_size',
546 report, 'manifest')
547 if actual_block_size != self.block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700548 raise error.PayloadError('Block_size (%d) not as expected (%d).' %
549 (actual_block_size, self.block_size))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800550
551 # Check: signatures_offset <==> signatures_size.
552 self.sigs_offset = self._CheckOptionalField(manifest, 'signatures_offset',
553 report)
554 self.sigs_size = self._CheckOptionalField(manifest, 'signatures_size',
555 report)
556 self._CheckPresentIff(self.sigs_offset, self.sigs_size,
557 'signatures_offset', 'signatures_size', 'manifest')
558
559 # Check: old_kernel_info <==> old_rootfs_info.
560 oki_msg, oki_report = self._CheckOptionalSubMsg(manifest,
561 'old_kernel_info', report)
562 ori_msg, ori_report = self._CheckOptionalSubMsg(manifest,
563 'old_rootfs_info', report)
564 self._CheckPresentIff(oki_msg, ori_msg, 'old_kernel_info',
565 'old_rootfs_info', 'manifest')
566 if oki_msg: # equivalently, ori_msg
567 # Assert/mark delta payload.
568 if self.payload_type == _TYPE_FULL:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700569 raise error.PayloadError(
570 'Apparent full payload contains old_{kernel,rootfs}_info.')
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800571 self.payload_type = _TYPE_DELTA
572
573 # Check: {size, hash} present in old_{kernel,rootfs}_info.
Gilad Arnold382df5c2013-05-03 12:49:28 -0700574 self.old_kernel_fs_size = self._CheckMandatoryField(
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800575 oki_msg, 'size', oki_report, 'old_kernel_info')
576 self._CheckMandatoryField(oki_msg, 'hash', oki_report, 'old_kernel_info',
577 convert=common.FormatSha256)
Gilad Arnold382df5c2013-05-03 12:49:28 -0700578 self.old_rootfs_fs_size = self._CheckMandatoryField(
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800579 ori_msg, 'size', ori_report, 'old_rootfs_info')
580 self._CheckMandatoryField(ori_msg, 'hash', ori_report, 'old_rootfs_info',
581 convert=common.FormatSha256)
Gilad Arnold382df5c2013-05-03 12:49:28 -0700582
583 # Check: old_{kernel,rootfs} size must fit in respective partition.
584 if kernel_part_size and self.old_kernel_fs_size > kernel_part_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700585 raise error.PayloadError(
586 'Old kernel content (%d) exceed partition size (%d).' %
Gilad Arnold382df5c2013-05-03 12:49:28 -0700587 (self.old_kernel_fs_size, kernel_part_size))
588 if rootfs_part_size and self.old_rootfs_fs_size > rootfs_part_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700589 raise error.PayloadError(
590 'Old rootfs content (%d) exceed partition size (%d).' %
Gilad Arnold382df5c2013-05-03 12:49:28 -0700591 (self.old_rootfs_fs_size, rootfs_part_size))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800592 else:
593 # Assert/mark full payload.
594 if self.payload_type == _TYPE_DELTA:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700595 raise error.PayloadError(
596 'Apparent delta payload missing old_{kernel,rootfs}_info.')
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800597 self.payload_type = _TYPE_FULL
598
599 # Check: new_kernel_info present; contains {size, hash}.
600 nki_msg, nki_report = self._CheckMandatorySubMsg(
601 manifest, 'new_kernel_info', report, 'manifest')
Gilad Arnold382df5c2013-05-03 12:49:28 -0700602 self.new_kernel_fs_size = self._CheckMandatoryField(
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800603 nki_msg, 'size', nki_report, 'new_kernel_info')
604 self._CheckMandatoryField(nki_msg, 'hash', nki_report, 'new_kernel_info',
605 convert=common.FormatSha256)
606
607 # Check: new_rootfs_info present; contains {size, hash}.
608 nri_msg, nri_report = self._CheckMandatorySubMsg(
609 manifest, 'new_rootfs_info', report, 'manifest')
Gilad Arnold382df5c2013-05-03 12:49:28 -0700610 self.new_rootfs_fs_size = self._CheckMandatoryField(
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800611 nri_msg, 'size', nri_report, 'new_rootfs_info')
612 self._CheckMandatoryField(nri_msg, 'hash', nri_report, 'new_rootfs_info',
613 convert=common.FormatSha256)
614
Gilad Arnold382df5c2013-05-03 12:49:28 -0700615 # Check: new_{kernel,rootfs} size must fit in respective partition.
616 if kernel_part_size and self.new_kernel_fs_size > kernel_part_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700617 raise error.PayloadError(
618 'New kernel content (%d) exceed partition size (%d).' %
Gilad Arnold382df5c2013-05-03 12:49:28 -0700619 (self.new_kernel_fs_size, kernel_part_size))
620 if rootfs_part_size and self.new_rootfs_fs_size > rootfs_part_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700621 raise error.PayloadError(
622 'New rootfs content (%d) exceed partition size (%d).' %
Gilad Arnold382df5c2013-05-03 12:49:28 -0700623 (self.new_rootfs_fs_size, rootfs_part_size))
624
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800625 # Check: minor_version makes sense for the payload type. This check should
626 # run after the payload type has been set.
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700627 self._CheckManifestMinorVersion(report)
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800628
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800629 def _CheckLength(self, length, total_blocks, op_name, length_name):
630 """Checks whether a length matches the space designated in extents.
631
632 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700633 length: The total length of the data.
634 total_blocks: The total number of blocks in extents.
635 op_name: Operation name (for error reporting).
636 length_name: Length name (for error reporting).
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800637
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800638 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700639 error.PayloadError is there a problem with the length.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800640 """
641 # Check: length is non-zero.
642 if length == 0:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700643 raise error.PayloadError('%s: %s is zero.' % (op_name, length_name))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800644
645 # Check that length matches number of blocks.
646 self._CheckBlocksFitLength(length, total_blocks, self.block_size,
647 '%s: %s' % (op_name, length_name))
648
Gilad Arnold382df5c2013-05-03 12:49:28 -0700649 def _CheckExtents(self, extents, usable_size, block_counters, name,
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800650 allow_pseudo=False, allow_signature=False):
651 """Checks a sequence of extents.
652
653 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700654 extents: The sequence of extents to check.
655 usable_size: The usable size of the partition to which the extents apply.
656 block_counters: Array of counters corresponding to the number of blocks.
657 name: The name of the extent block.
658 allow_pseudo: Whether or not pseudo block numbers are allowed.
659 allow_signature: Whether or not the extents are used for a signature.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800660
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800661 Returns:
662 The total number of blocks in the extents.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800663
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800664 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700665 error.PayloadError if any of the entailed checks fails.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800666 """
667 total_num_blocks = 0
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800668 for ex, ex_name in common.ExtentIter(extents, name):
Gilad Arnoldcb638912013-06-24 04:57:11 -0700669 # Check: Mandatory fields.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800670 start_block = PayloadChecker._CheckMandatoryField(ex, 'start_block',
671 None, ex_name)
672 num_blocks = PayloadChecker._CheckMandatoryField(ex, 'num_blocks', None,
673 ex_name)
674 end_block = start_block + num_blocks
675
676 # Check: num_blocks > 0.
677 if num_blocks == 0:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700678 raise error.PayloadError('%s: extent length is zero.' % ex_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800679
680 if start_block != common.PSEUDO_EXTENT_MARKER:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700681 # Check: Make sure we're within the partition limit.
Gilad Arnold382df5c2013-05-03 12:49:28 -0700682 if usable_size and end_block * self.block_size > usable_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700683 raise error.PayloadError(
684 '%s: extent (%s) exceeds usable partition size (%d).' %
Gilad Arnold382df5c2013-05-03 12:49:28 -0700685 (ex_name, common.FormatExtent(ex, self.block_size), usable_size))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800686
687 # Record block usage.
Gilad Arnoldcb638912013-06-24 04:57:11 -0700688 for i in xrange(start_block, end_block):
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800689 block_counters[i] += 1
Gilad Arnold5502b562013-03-08 13:22:31 -0800690 elif not (allow_pseudo or (allow_signature and len(extents) == 1)):
691 # Pseudo-extents must be allowed explicitly, or otherwise be part of a
692 # signature operation (in which case there has to be exactly one).
Gilad Arnoldcb638912013-06-24 04:57:11 -0700693 raise error.PayloadError('%s: unexpected pseudo-extent.' % ex_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800694
695 total_num_blocks += num_blocks
696
697 return total_num_blocks
698
699 def _CheckReplaceOperation(self, op, data_length, total_dst_blocks, op_name):
700 """Specific checks for REPLACE/REPLACE_BZ operations.
701
702 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700703 op: The operation object from the manifest.
704 data_length: The length of the data blob associated with the operation.
705 total_dst_blocks: Total number of blocks in dst_extents.
706 op_name: Operation name for error reporting.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800707
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800708 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700709 error.PayloadError if any check fails.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800710 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700711 # Check: Does not contain src extents.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800712 if op.src_extents:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700713 raise error.PayloadError('%s: contains src_extents.' % op_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800714
Gilad Arnoldcb638912013-06-24 04:57:11 -0700715 # Check: Contains data.
Gilad Arnold5502b562013-03-08 13:22:31 -0800716 if data_length is None:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700717 raise error.PayloadError('%s: missing data_{offset,length}.' % op_name)
Gilad Arnold5502b562013-03-08 13:22:31 -0800718
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800719 if op.type == common.OpType.REPLACE:
720 PayloadChecker._CheckBlocksFitLength(data_length, total_dst_blocks,
721 self.block_size,
722 op_name + '.data_length', 'dst')
723 else:
724 # Check: data_length must be smaller than the alotted dst blocks.
725 if data_length >= total_dst_blocks * self.block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700726 raise error.PayloadError(
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800727 '%s: data_length (%d) must be less than allotted dst block '
Gilad Arnoldcb638912013-06-24 04:57:11 -0700728 'space (%d * %d).' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800729 (op_name, data_length, total_dst_blocks, self.block_size))
730
731 def _CheckMoveOperation(self, op, data_offset, total_src_blocks,
732 total_dst_blocks, op_name):
733 """Specific checks for MOVE operations.
734
735 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700736 op: The operation object from the manifest.
737 data_offset: The offset of a data blob for the operation.
738 total_src_blocks: Total number of blocks in src_extents.
739 total_dst_blocks: Total number of blocks in dst_extents.
740 op_name: Operation name for error reporting.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800741
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800742 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700743 error.PayloadError if any check fails.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800744 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700745 # Check: No data_{offset,length}.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800746 if data_offset is not None:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700747 raise error.PayloadError('%s: contains data_{offset,length}.' % op_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800748
Gilad Arnoldcb638912013-06-24 04:57:11 -0700749 # Check: total_src_blocks == total_dst_blocks.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800750 if total_src_blocks != total_dst_blocks:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700751 raise error.PayloadError(
752 '%s: total src blocks (%d) != total dst blocks (%d).' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800753 (op_name, total_src_blocks, total_dst_blocks))
754
Gilad Arnoldcb638912013-06-24 04:57:11 -0700755 # Check: For all i, i-th src block index != i-th dst block index.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800756 i = 0
757 src_extent_iter = iter(op.src_extents)
758 dst_extent_iter = iter(op.dst_extents)
759 src_extent = dst_extent = None
760 src_idx = src_num = dst_idx = dst_num = 0
761 while i < total_src_blocks:
762 # Get the next source extent, if needed.
763 if not src_extent:
764 try:
765 src_extent = src_extent_iter.next()
766 except StopIteration:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700767 raise error.PayloadError('%s: ran out of src extents (%d/%d).' %
768 (op_name, i, total_src_blocks))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800769 src_idx = src_extent.start_block
770 src_num = src_extent.num_blocks
771
772 # Get the next dest extent, if needed.
773 if not dst_extent:
774 try:
775 dst_extent = dst_extent_iter.next()
776 except StopIteration:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700777 raise error.PayloadError('%s: ran out of dst extents (%d/%d).' %
778 (op_name, i, total_dst_blocks))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800779 dst_idx = dst_extent.start_block
780 dst_num = dst_extent.num_blocks
781
Allie Woodb065e132015-04-24 10:20:27 -0700782 # Check: start block is not 0. See crbug/480751; there are still versions
783 # of update_engine which fail when seeking to 0 in PReadAll and PWriteAll,
784 # so we need to fail payloads that try to MOVE to/from block 0.
785 if src_idx == 0 or dst_idx == 0:
786 raise error.PayloadError(
787 '%s: MOVE operation cannot have extent with start block 0' %
788 op_name)
789
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700790 if self.check_move_same_src_dst_block and src_idx == dst_idx:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700791 raise error.PayloadError(
792 '%s: src/dst block number %d is the same (%d).' %
793 (op_name, i, src_idx))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800794
795 advance = min(src_num, dst_num)
796 i += advance
797
798 src_idx += advance
799 src_num -= advance
800 if src_num == 0:
801 src_extent = None
802
803 dst_idx += advance
804 dst_num -= advance
805 if dst_num == 0:
806 dst_extent = None
807
Gilad Arnold5502b562013-03-08 13:22:31 -0800808 # Make sure we've exhausted all src/dst extents.
809 if src_extent:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700810 raise error.PayloadError('%s: excess src blocks.' % op_name)
Gilad Arnold5502b562013-03-08 13:22:31 -0800811 if dst_extent:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700812 raise error.PayloadError('%s: excess dst blocks.' % op_name)
Gilad Arnold5502b562013-03-08 13:22:31 -0800813
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800814 def _CheckBsdiffOperation(self, data_length, total_dst_blocks, op_name):
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800815 """Specific checks for BSDIFF and SOURCE_BSDIFF operations.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800816
817 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700818 data_length: The length of the data blob associated with the operation.
819 total_dst_blocks: Total number of blocks in dst_extents.
820 op_name: Operation name for error reporting.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800821
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800822 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700823 error.PayloadError if any check fails.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800824 """
Gilad Arnold5502b562013-03-08 13:22:31 -0800825 # Check: data_{offset,length} present.
826 if data_length is None:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700827 raise error.PayloadError('%s: missing data_{offset,length}.' % op_name)
Gilad Arnold5502b562013-03-08 13:22:31 -0800828
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800829 # Check: data_length is strictly smaller than the alotted dst blocks.
830 if data_length >= total_dst_blocks * self.block_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700831 raise error.PayloadError(
Gilad Arnold5502b562013-03-08 13:22:31 -0800832 '%s: data_length (%d) must be smaller than allotted dst space '
Gilad Arnoldcb638912013-06-24 04:57:11 -0700833 '(%d * %d = %d).' %
Gilad Arnold5502b562013-03-08 13:22:31 -0800834 (op_name, data_length, total_dst_blocks, self.block_size,
835 total_dst_blocks * self.block_size))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800836
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800837 def _CheckSourceCopyOperation(self, data_offset, total_src_blocks,
838 total_dst_blocks, op_name):
839 """Specific checks for SOURCE_COPY.
840
841 Args:
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800842 data_offset: The offset of a data blob for the operation.
843 total_src_blocks: Total number of blocks in src_extents.
844 total_dst_blocks: Total number of blocks in dst_extents.
845 op_name: Operation name for error reporting.
846
847 Raises:
848 error.PayloadError if any check fails.
849 """
850 # Check: No data_{offset,length}.
851 if data_offset is not None:
852 raise error.PayloadError('%s: contains data_{offset,length}.' % op_name)
853
854 # Check: total_src_blocks == total_dst_blocks.
855 if total_src_blocks != total_dst_blocks:
856 raise error.PayloadError(
857 '%s: total src blocks (%d) != total dst blocks (%d).' %
858 (op_name, total_src_blocks, total_dst_blocks))
859
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800860 def _CheckOperation(self, op, op_name, is_last, old_block_counters,
Gilad Arnold4f50b412013-05-14 09:19:17 -0700861 new_block_counters, old_usable_size, new_usable_size,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700862 prev_data_offset, allow_signature, blob_hash_counts):
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800863 """Checks a single update operation.
864
865 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700866 op: The operation object.
867 op_name: Operation name string for error reporting.
868 is_last: Whether this is the last operation in the sequence.
869 old_block_counters: Arrays of block read counters.
870 new_block_counters: Arrays of block write counters.
871 old_usable_size: The overall usable size for src data in bytes.
872 new_usable_size: The overall usable size for dst data in bytes.
873 prev_data_offset: Offset of last used data bytes.
874 allow_signature: Whether this may be a signature operation.
875 blob_hash_counts: Counters for hashed/unhashed blobs.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800876
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800877 Returns:
878 The amount of data blob associated with the operation.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800879
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800880 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700881 error.PayloadError if any check has failed.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800882 """
883 # Check extents.
884 total_src_blocks = self._CheckExtents(
Gilad Arnold4f50b412013-05-14 09:19:17 -0700885 op.src_extents, old_usable_size, old_block_counters,
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800886 op_name + '.src_extents', allow_pseudo=True)
887 allow_signature_in_extents = (allow_signature and is_last and
888 op.type == common.OpType.REPLACE)
889 total_dst_blocks = self._CheckExtents(
Gilad Arnold382df5c2013-05-03 12:49:28 -0700890 op.dst_extents, new_usable_size, new_block_counters,
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700891 op_name + '.dst_extents',
892 allow_pseudo=(not self.check_dst_pseudo_extents),
893 allow_signature=allow_signature_in_extents)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800894
895 # Check: data_offset present <==> data_length present.
896 data_offset = self._CheckOptionalField(op, 'data_offset', None)
897 data_length = self._CheckOptionalField(op, 'data_length', None)
898 self._CheckPresentIff(data_offset, data_length, 'data_offset',
899 'data_length', op_name)
900
Gilad Arnoldcb638912013-06-24 04:57:11 -0700901 # Check: At least one dst_extent.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800902 if not op.dst_extents:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700903 raise error.PayloadError('%s: dst_extents is empty.' % op_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800904
905 # Check {src,dst}_length, if present.
906 if op.HasField('src_length'):
907 self._CheckLength(op.src_length, total_src_blocks, op_name, 'src_length')
908 if op.HasField('dst_length'):
909 self._CheckLength(op.dst_length, total_dst_blocks, op_name, 'dst_length')
910
911 if op.HasField('data_sha256_hash'):
912 blob_hash_counts['hashed'] += 1
913
Gilad Arnoldcb638912013-06-24 04:57:11 -0700914 # Check: Operation carries data.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800915 if data_offset is None:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700916 raise error.PayloadError(
917 '%s: data_sha256_hash present but no data_{offset,length}.' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800918 op_name)
919
Gilad Arnoldcb638912013-06-24 04:57:11 -0700920 # Check: Hash verifies correctly.
921 # pylint cannot find the method in hashlib, for some reason.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800922 # pylint: disable=E1101
923 actual_hash = hashlib.sha256(self.payload.ReadDataBlob(data_offset,
924 data_length))
925 if op.data_sha256_hash != actual_hash.digest():
Gilad Arnoldcb638912013-06-24 04:57:11 -0700926 raise error.PayloadError(
927 '%s: data_sha256_hash (%s) does not match actual hash (%s).' %
Gilad Arnold96405372013-05-04 00:24:58 -0700928 (op_name, common.FormatSha256(op.data_sha256_hash),
929 common.FormatSha256(actual_hash.digest())))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800930 elif data_offset is not None:
931 if allow_signature_in_extents:
932 blob_hash_counts['signature'] += 1
Gilad Arnoldeaed0d12013-04-30 15:38:22 -0700933 elif self.allow_unhashed:
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800934 blob_hash_counts['unhashed'] += 1
935 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700936 raise error.PayloadError('%s: unhashed operation not allowed.' %
937 op_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800938
939 if data_offset is not None:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700940 # Check: Contiguous use of data section.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800941 if data_offset != prev_data_offset:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700942 raise error.PayloadError(
943 '%s: data offset (%d) not matching amount used so far (%d).' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800944 (op_name, data_offset, prev_data_offset))
945
946 # Type-specific checks.
947 if op.type in (common.OpType.REPLACE, common.OpType.REPLACE_BZ):
948 self._CheckReplaceOperation(op, data_length, total_dst_blocks, op_name)
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700949 elif op.type == common.OpType.MOVE and self.minor_version == 1:
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800950 self._CheckMoveOperation(op, data_offset, total_src_blocks,
951 total_dst_blocks, op_name)
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700952 elif op.type == common.OpType.BSDIFF and self.minor_version == 1:
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800953 self._CheckBsdiffOperation(data_length, total_dst_blocks, op_name)
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700954 elif op.type == common.OpType.SOURCE_COPY and self.minor_version == 2:
Allie Woodf5c4f3e2015-02-20 16:57:46 -0800955 self._CheckSourceCopyOperation(data_offset, total_src_blocks,
956 total_dst_blocks, op_name)
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700957 elif op.type == common.OpType.SOURCE_BSDIFF and self.minor_version == 2:
Allie Wood7cf9f132015-02-26 14:28:19 -0800958 self._CheckBsdiffOperation(data_length, total_dst_blocks, op_name)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800959 else:
Allie Wood7cf9f132015-02-26 14:28:19 -0800960 raise error.PayloadError(
961 'Operation %s (type %d) not allowed in minor version %d' %
Gilad Arnold0d575cd2015-07-13 17:29:21 -0700962 (op_name, op.type, self.minor_version))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800963 return data_length if data_length is not None else 0
964
Gilad Arnold382df5c2013-05-03 12:49:28 -0700965 def _SizeToNumBlocks(self, size):
966 """Returns the number of blocks needed to contain a given byte size."""
967 return (size + self.block_size - 1) / self.block_size
968
969 def _AllocBlockCounters(self, total_size):
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800970 """Returns a freshly initialized array of block counters.
971
Gilad Arnoldcb638912013-06-24 04:57:11 -0700972 Note that the generated array is not portable as is due to byte-ordering
973 issues, hence it should not be serialized.
974
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800975 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700976 total_size: The total block size in bytes.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800977
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800978 Returns:
Gilad Arnold9753f3d2013-07-23 08:34:45 -0700979 An array of unsigned short elements initialized to zero, one for each of
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800980 the blocks necessary for containing the partition.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800981 """
Gilad Arnoldcb638912013-06-24 04:57:11 -0700982 return array.array('H',
983 itertools.repeat(0, self._SizeToNumBlocks(total_size)))
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800984
Gilad Arnold382df5c2013-05-03 12:49:28 -0700985 def _CheckOperations(self, operations, report, base_name, old_fs_size,
986 new_fs_size, new_usable_size, prev_data_offset,
987 allow_signature):
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800988 """Checks a sequence of update operations.
989
990 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -0700991 operations: The sequence of operations to check.
992 report: The report object to add to.
993 base_name: The name of the operation block.
994 old_fs_size: The old filesystem size in bytes.
995 new_fs_size: The new filesystem size in bytes.
996 new_usable_size: The overall usable size of the new partition in bytes.
997 prev_data_offset: Offset of last used data bytes.
998 allow_signature: Whether this sequence may contain signature operations.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -0800999
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001000 Returns:
Gilad Arnold5502b562013-03-08 13:22:31 -08001001 The total data blob size used.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -08001002
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001003 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001004 error.PayloadError if any of the checks fails.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001005 """
1006 # The total size of data blobs used by operations scanned thus far.
1007 total_data_used = 0
1008 # Counts of specific operation types.
1009 op_counts = {
1010 common.OpType.REPLACE: 0,
1011 common.OpType.REPLACE_BZ: 0,
1012 common.OpType.MOVE: 0,
1013 common.OpType.BSDIFF: 0,
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001014 common.OpType.SOURCE_COPY: 0,
1015 common.OpType.SOURCE_BSDIFF: 0,
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001016 }
1017 # Total blob sizes for each operation type.
1018 op_blob_totals = {
1019 common.OpType.REPLACE: 0,
1020 common.OpType.REPLACE_BZ: 0,
Gilad Arnoldcb638912013-06-24 04:57:11 -07001021 # MOVE operations don't have blobs.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001022 common.OpType.BSDIFF: 0,
Allie Woodf5c4f3e2015-02-20 16:57:46 -08001023 # SOURCE_COPY operations don't have blobs.
1024 common.OpType.SOURCE_BSDIFF: 0,
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001025 }
1026 # Counts of hashed vs unhashed operations.
1027 blob_hash_counts = {
1028 'hashed': 0,
1029 'unhashed': 0,
1030 }
1031 if allow_signature:
1032 blob_hash_counts['signature'] = 0
1033
1034 # Allocate old and new block counters.
Gilad Arnold4f50b412013-05-14 09:19:17 -07001035 old_block_counters = (self._AllocBlockCounters(new_usable_size)
Gilad Arnold382df5c2013-05-03 12:49:28 -07001036 if old_fs_size else None)
1037 new_block_counters = self._AllocBlockCounters(new_usable_size)
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001038
1039 # Process and verify each operation.
1040 op_num = 0
1041 for op, op_name in common.OperationIter(operations, base_name):
1042 op_num += 1
1043
Gilad Arnoldcb638912013-06-24 04:57:11 -07001044 # Check: Type is valid.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001045 if op.type not in op_counts.keys():
Gilad Arnoldcb638912013-06-24 04:57:11 -07001046 raise error.PayloadError('%s: invalid type (%d).' % (op_name, op.type))
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001047 op_counts[op.type] += 1
1048
1049 is_last = op_num == len(operations)
1050 curr_data_used = self._CheckOperation(
1051 op, op_name, is_last, old_block_counters, new_block_counters,
Gilad Arnold4f50b412013-05-14 09:19:17 -07001052 new_usable_size if old_fs_size else 0, new_usable_size,
1053 prev_data_offset + total_data_used, allow_signature,
1054 blob_hash_counts)
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001055 if curr_data_used:
1056 op_blob_totals[op.type] += curr_data_used
1057 total_data_used += curr_data_used
1058
1059 # Report totals and breakdown statistics.
1060 report.AddField('total operations', op_num)
1061 report.AddField(
1062 None,
1063 histogram.Histogram.FromCountDict(op_counts,
1064 key_names=common.OpType.NAMES),
1065 indent=1)
1066 report.AddField('total blobs', sum(blob_hash_counts.values()))
1067 report.AddField(None,
1068 histogram.Histogram.FromCountDict(blob_hash_counts),
1069 indent=1)
1070 report.AddField('total blob size', _AddHumanReadableSize(total_data_used))
1071 report.AddField(
1072 None,
1073 histogram.Histogram.FromCountDict(op_blob_totals,
1074 formatter=_AddHumanReadableSize,
1075 key_names=common.OpType.NAMES),
1076 indent=1)
1077
1078 # Report read/write histograms.
1079 if old_block_counters:
1080 report.AddField('block read hist',
1081 histogram.Histogram.FromKeyList(old_block_counters),
1082 linebreak=True, indent=1)
1083
Gilad Arnold382df5c2013-05-03 12:49:28 -07001084 new_write_hist = histogram.Histogram.FromKeyList(
1085 new_block_counters[:self._SizeToNumBlocks(new_fs_size)])
1086 report.AddField('block write hist', new_write_hist, linebreak=True,
1087 indent=1)
1088
Gilad Arnoldcb638912013-06-24 04:57:11 -07001089 # Check: Full update must write each dst block once.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001090 if self.payload_type == _TYPE_FULL and new_write_hist.GetKeys() != [1]:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001091 raise error.PayloadError(
1092 '%s: not all blocks written exactly once during full update.' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001093 base_name)
1094
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001095 return total_data_used
1096
1097 def _CheckSignatures(self, report, pubkey_file_name):
1098 """Checks a payload's signature block."""
1099 sigs_raw = self.payload.ReadDataBlob(self.sigs_offset, self.sigs_size)
1100 sigs = update_metadata_pb2.Signatures()
1101 sigs.ParseFromString(sigs_raw)
1102 report.AddSection('signatures')
1103
Gilad Arnoldcb638912013-06-24 04:57:11 -07001104 # Check: At least one signature present.
1105 # pylint cannot see through the protobuf object, it seems.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001106 # pylint: disable=E1101
1107 if not sigs.signatures:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001108 raise error.PayloadError('Signature block is empty.')
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001109
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001110 last_ops_section = (self.payload.manifest.kernel_install_operations or
1111 self.payload.manifest.install_operations)
1112 fake_sig_op = last_ops_section[-1]
Gilad Arnold5502b562013-03-08 13:22:31 -08001113 # Check: signatures_{offset,size} must match the last (fake) operation.
1114 if not (fake_sig_op.type == common.OpType.REPLACE and
1115 self.sigs_offset == fake_sig_op.data_offset and
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001116 self.sigs_size == fake_sig_op.data_length):
Gilad Arnoldcb638912013-06-24 04:57:11 -07001117 raise error.PayloadError(
1118 'Signatures_{offset,size} (%d+%d) does not match last operation '
1119 '(%d+%d).' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001120 (self.sigs_offset, self.sigs_size, fake_sig_op.data_offset,
1121 fake_sig_op.data_length))
1122
1123 # Compute the checksum of all data up to signature blob.
1124 # TODO(garnold) we're re-reading the whole data section into a string
1125 # just to compute the checksum; instead, we could do it incrementally as
1126 # we read the blobs one-by-one, under the assumption that we're reading
1127 # them in order (which currently holds). This should be reconsidered.
1128 payload_hasher = self.payload.manifest_hasher.copy()
1129 common.Read(self.payload.payload_file, self.sigs_offset,
1130 offset=self.payload.data_offset, hasher=payload_hasher)
1131
1132 for sig, sig_name in common.SignatureIter(sigs.signatures, 'signatures'):
1133 sig_report = report.AddSubReport(sig_name)
1134
Gilad Arnoldcb638912013-06-24 04:57:11 -07001135 # Check: Signature contains mandatory fields.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001136 self._CheckMandatoryField(sig, 'version', sig_report, sig_name)
1137 self._CheckMandatoryField(sig, 'data', None, sig_name)
1138 sig_report.AddField('data len', len(sig.data))
1139
Gilad Arnoldcb638912013-06-24 04:57:11 -07001140 # Check: Signatures pertains to actual payload hash.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001141 if sig.version == 1:
1142 self._CheckSha256Signature(sig.data, pubkey_file_name,
1143 payload_hasher.digest(), sig_name)
1144 else:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001145 raise error.PayloadError('Unknown signature version (%d).' %
1146 sig.version)
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001147
1148 def Run(self, pubkey_file_name=None, metadata_sig_file=None,
Gilad Arnold382df5c2013-05-03 12:49:28 -07001149 rootfs_part_size=0, kernel_part_size=0, report_out_file=None):
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001150 """Checker entry point, invoking all checks.
1151
1152 Args:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001153 pubkey_file_name: Public key used for signature verification.
1154 metadata_sig_file: Metadata signature, if verification is desired.
1155 rootfs_part_size: The size of rootfs partitions in bytes (default: use
1156 reported filesystem size).
1157 kernel_part_size: The size of kernel partitions in bytes (default: use
1158 reported filesystem size).
1159 report_out_file: File object to dump the report to.
Gilad Arnoldf583a7d2015-02-05 13:23:55 -08001160
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001161 Raises:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001162 error.PayloadError if payload verification failed.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001163 """
Gilad Arnold9b90c932013-05-22 17:12:56 -07001164 if not pubkey_file_name:
1165 pubkey_file_name = _DEFAULT_PUBKEY_FILE_NAME
1166
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001167 report = _PayloadReport()
1168
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001169 # Get payload file size.
1170 self.payload.payload_file.seek(0, 2)
1171 payload_file_size = self.payload.payload_file.tell()
1172 self.payload.ResetFile()
1173
1174 try:
1175 # Check metadata signature (if provided).
1176 if metadata_sig_file:
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001177 metadata_sig = base64.b64decode(metadata_sig_file.read())
1178 self._CheckSha256Signature(metadata_sig, pubkey_file_name,
1179 self.payload.manifest_hasher.digest(),
1180 'metadata signature')
1181
Gilad Arnoldcb638912013-06-24 04:57:11 -07001182 # Part 1: Check the file header.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001183 report.AddSection('header')
Gilad Arnoldcb638912013-06-24 04:57:11 -07001184 # Check: Payload version is valid.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001185 if self.payload.header.version != 1:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001186 raise error.PayloadError('Unknown payload version (%d).' %
1187 self.payload.header.version)
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001188 report.AddField('version', self.payload.header.version)
1189 report.AddField('manifest len', self.payload.header.manifest_len)
1190
Gilad Arnoldcb638912013-06-24 04:57:11 -07001191 # Part 2: Check the manifest.
Gilad Arnold382df5c2013-05-03 12:49:28 -07001192 self._CheckManifest(report, rootfs_part_size, kernel_part_size)
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001193 assert self.payload_type, 'payload type should be known by now'
1194
Gilad Arnoldcb638912013-06-24 04:57:11 -07001195 # Part 3: Examine rootfs operations.
Gilad Arnold0990f512013-05-30 17:09:31 -07001196 # TODO(garnold)(chromium:243559) only default to the filesystem size if
1197 # no explicit size provided *and* the partition size is not embedded in
1198 # the payload; see issue for more details.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001199 report.AddSection('rootfs operations')
1200 total_blob_size = self._CheckOperations(
1201 self.payload.manifest.install_operations, report,
Gilad Arnold382df5c2013-05-03 12:49:28 -07001202 'install_operations', self.old_rootfs_fs_size,
1203 self.new_rootfs_fs_size,
1204 rootfs_part_size if rootfs_part_size else self.new_rootfs_fs_size,
1205 0, False)
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001206
Gilad Arnoldcb638912013-06-24 04:57:11 -07001207 # Part 4: Examine kernel operations.
Gilad Arnold0990f512013-05-30 17:09:31 -07001208 # TODO(garnold)(chromium:243559) as above.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001209 report.AddSection('kernel operations')
1210 total_blob_size += self._CheckOperations(
1211 self.payload.manifest.kernel_install_operations, report,
Gilad Arnold382df5c2013-05-03 12:49:28 -07001212 'kernel_install_operations', self.old_kernel_fs_size,
1213 self.new_kernel_fs_size,
1214 kernel_part_size if kernel_part_size else self.new_kernel_fs_size,
1215 total_blob_size, True)
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001216
Gilad Arnoldcb638912013-06-24 04:57:11 -07001217 # Check: Operations data reach the end of the payload file.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001218 used_payload_size = self.payload.data_offset + total_blob_size
1219 if used_payload_size != payload_file_size:
Gilad Arnoldcb638912013-06-24 04:57:11 -07001220 raise error.PayloadError(
1221 'Used payload size (%d) different from actual file size (%d).' %
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001222 (used_payload_size, payload_file_size))
1223
Gilad Arnoldcb638912013-06-24 04:57:11 -07001224 # Part 5: Handle payload signatures message.
Gilad Arnoldeaed0d12013-04-30 15:38:22 -07001225 if self.check_payload_sig and self.sigs_size:
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001226 self._CheckSignatures(report, pubkey_file_name)
1227
Gilad Arnoldcb638912013-06-24 04:57:11 -07001228 # Part 6: Summary.
Gilad Arnold553b0ec2013-01-26 01:00:39 -08001229 report.AddSection('summary')
1230 report.AddField('update type', self.payload_type)
1231
1232 report.Finalize()
1233 finally:
1234 if report_out_file:
1235 report.Dump(report_out_file)