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