• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 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"""Utilities for update payload processing."""
6
7from __future__ import print_function
8
9from update_payload import update_metadata_pb2
10from update_payload.error import PayloadError
11
12
13#
14# Constants.
15#
16PSEUDO_EXTENT_MARKER = (1L << 64) - 1  # UINT64_MAX
17
18SIG_ASN1_HEADER = (
19    '\x30\x31\x30\x0d\x06\x09\x60\x86'
20    '\x48\x01\x65\x03\x04\x02\x01\x05'
21    '\x00\x04\x20'
22)
23
24CHROMEOS_MAJOR_PAYLOAD_VERSION = 1
25BRILLO_MAJOR_PAYLOAD_VERSION = 2
26
27INPLACE_MINOR_PAYLOAD_VERSION = 1
28SOURCE_MINOR_PAYLOAD_VERSION = 2
29OPSRCHASH_MINOR_PAYLOAD_VERSION = 3
30BROTLI_BSDIFF_MINOR_PAYLOAD_VERSION = 4
31PUFFDIFF_MINOR_PAYLOAD_VERSION = 5
32
33#
34# Payload operation types.
35#
36class OpType(object):
37  """Container for operation type constants."""
38  _CLASS = update_metadata_pb2.InstallOperation
39  REPLACE = _CLASS.REPLACE
40  REPLACE_BZ = _CLASS.REPLACE_BZ
41  MOVE = _CLASS.MOVE
42  BSDIFF = _CLASS.BSDIFF
43  SOURCE_COPY = _CLASS.SOURCE_COPY
44  SOURCE_BSDIFF = _CLASS.SOURCE_BSDIFF
45  ZERO = _CLASS.ZERO
46  DISCARD = _CLASS.DISCARD
47  REPLACE_XZ = _CLASS.REPLACE_XZ
48  PUFFDIFF = _CLASS.PUFFDIFF
49  BROTLI_BSDIFF = _CLASS.BROTLI_BSDIFF
50  ALL = (REPLACE, REPLACE_BZ, MOVE, BSDIFF, SOURCE_COPY, SOURCE_BSDIFF, ZERO,
51         DISCARD, REPLACE_XZ, PUFFDIFF, BROTLI_BSDIFF)
52  NAMES = {
53      REPLACE: 'REPLACE',
54      REPLACE_BZ: 'REPLACE_BZ',
55      MOVE: 'MOVE',
56      BSDIFF: 'BSDIFF',
57      SOURCE_COPY: 'SOURCE_COPY',
58      SOURCE_BSDIFF: 'SOURCE_BSDIFF',
59      ZERO: 'ZERO',
60      DISCARD: 'DISCARD',
61      REPLACE_XZ: 'REPLACE_XZ',
62      PUFFDIFF: 'PUFFDIFF',
63      BROTLI_BSDIFF: 'BROTLI_BSDIFF',
64  }
65
66  def __init__(self):
67    pass
68
69
70#
71# Checked and hashed reading of data.
72#
73def IntPackingFmtStr(size, is_unsigned):
74  """Returns an integer format string for use by the struct module.
75
76  Args:
77    size: the integer size in bytes (2, 4 or 8)
78    is_unsigned: whether it is signed or not
79
80  Returns:
81    A format string for packing/unpacking integer values; assumes network byte
82    order (big-endian).
83
84  Raises:
85    PayloadError if something is wrong with the arguments.
86  """
87  # Determine the base conversion format.
88  if size == 2:
89    fmt = 'h'
90  elif size == 4:
91    fmt = 'i'
92  elif size == 8:
93    fmt = 'q'
94  else:
95    raise PayloadError('unsupport numeric field size (%s)' % size)
96
97  # Signed or unsigned?
98  if is_unsigned:
99    fmt = fmt.upper()
100
101  # Make it network byte order (big-endian).
102  fmt = '!' + fmt
103
104  return fmt
105
106
107def Read(file_obj, length, offset=None, hasher=None):
108  """Reads binary data from a file.
109
110  Args:
111    file_obj: an open file object
112    length: the length of the data to read
113    offset: an offset to seek to prior to reading; this is an absolute offset
114            from either the beginning (non-negative) or end (negative) of the
115            file.  (optional)
116    hasher: a hashing object to pass the read data through (optional)
117
118  Returns:
119    A string containing the read data.
120
121  Raises:
122    PayloadError if a read error occurred or not enough data was read.
123  """
124  if offset is not None:
125    if offset >= 0:
126      file_obj.seek(offset)
127    else:
128      file_obj.seek(offset, 2)
129
130  try:
131    data = file_obj.read(length)
132  except IOError, e:
133    raise PayloadError('error reading from file (%s): %s' % (file_obj.name, e))
134
135  if len(data) != length:
136    raise PayloadError(
137        'reading from file (%s) too short (%d instead of %d bytes)' %
138        (file_obj.name, len(data), length))
139
140  if hasher:
141    hasher.update(data)
142
143  return data
144
145
146#
147# Formatting functions.
148#
149def FormatExtent(ex, block_size=0):
150  end_block = ex.start_block + ex.num_blocks
151  if block_size:
152    return '%d->%d * %d' % (ex.start_block, end_block, block_size)
153  else:
154    return '%d->%d' % (ex.start_block, end_block)
155
156
157def FormatSha256(digest):
158  """Returns a canonical string representation of a SHA256 digest."""
159  return digest.encode('base64').strip()
160
161
162#
163# Useful iterators.
164#
165def _ObjNameIter(items, base_name, reverse=False, name_format_func=None):
166  """A generic (item, name) tuple iterators.
167
168  Args:
169    items: the sequence of objects to iterate on
170    base_name: the base name for all objects
171    reverse: whether iteration should be in reverse order
172    name_format_func: a function to apply to the name string
173
174  Yields:
175    An iterator whose i-th invocation returns (items[i], name), where name ==
176    base_name + '[i]' (with a formatting function optionally applied to it).
177  """
178  idx, inc = (len(items), -1) if reverse else (1, 1)
179  if reverse:
180    items = reversed(items)
181  for item in items:
182    item_name = '%s[%d]' % (base_name, idx)
183    if name_format_func:
184      item_name = name_format_func(item, item_name)
185    yield (item, item_name)
186    idx += inc
187
188
189def _OperationNameFormatter(op, op_name):
190  return '%s(%s)' % (op_name, OpType.NAMES.get(op.type, '?'))
191
192
193def OperationIter(operations, base_name, reverse=False):
194  """An (item, name) iterator for update operations."""
195  return _ObjNameIter(operations, base_name, reverse=reverse,
196                      name_format_func=_OperationNameFormatter)
197
198
199def ExtentIter(extents, base_name, reverse=False):
200  """An (item, name) iterator for operation extents."""
201  return _ObjNameIter(extents, base_name, reverse=reverse)
202
203
204def SignatureIter(sigs, base_name, reverse=False):
205  """An (item, name) iterator for signatures."""
206  return _ObjNameIter(sigs, base_name, reverse=reverse)
207