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