blob: ac05ccdc69785faea4723b4a2544519e28220286 [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"""Utilities for update payload processing."""
6
Allie Wood12f59aa2015-04-06 11:05:12 -07007from __future__ import print_function
8
Amin Hassanib05a65a2017-12-18 15:15:32 -08009from update_payload import update_metadata_pb2
10from update_payload.error import PayloadError
Gilad Arnold553b0ec2013-01-26 01:00:39 -080011
12
13#
14# Constants.
15#
Alex Deymocf6f30d2015-06-11 13:51:46 -070016PSEUDO_EXTENT_MARKER = (1L << 64) - 1 # UINT64_MAX
Gilad Arnold553b0ec2013-01-26 01:00:39 -080017
Gilad Arnold5502b562013-03-08 13:22:31 -080018SIG_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
Alex Deymoef497352015-10-15 09:14:58 -070024CHROMEOS_MAJOR_PAYLOAD_VERSION = 1
25BRILLO_MAJOR_PAYLOAD_VERSION = 2
26
Allie Wood12f59aa2015-04-06 11:05:12 -070027INPLACE_MINOR_PAYLOAD_VERSION = 1
28SOURCE_MINOR_PAYLOAD_VERSION = 2
Sen Jiangd6122bb2015-12-11 10:27:04 -080029OPSRCHASH_MINOR_PAYLOAD_VERSION = 3
Amin Hassani90c57d72018-02-07 16:21:33 -080030BROTLI_BSDIFF_MINOR_PAYLOAD_VERSION = 4
31PUFFDIFF_MINOR_PAYLOAD_VERSION = 5
Gilad Arnold553b0ec2013-01-26 01:00:39 -080032
33#
34# Payload operation types.
35#
36class OpType(object):
37 """Container for operation type constants."""
Alex Deymo28466772015-09-11 17:16:44 -070038 _CLASS = update_metadata_pb2.InstallOperation
Gilad Arnold553b0ec2013-01-26 01:00:39 -080039 REPLACE = _CLASS.REPLACE
40 REPLACE_BZ = _CLASS.REPLACE_BZ
41 MOVE = _CLASS.MOVE
42 BSDIFF = _CLASS.BSDIFF
Allie Woodc11dc732015-02-18 15:53:05 -080043 SOURCE_COPY = _CLASS.SOURCE_COPY
44 SOURCE_BSDIFF = _CLASS.SOURCE_BSDIFF
Alex Deymo28466772015-09-11 17:16:44 -070045 ZERO = _CLASS.ZERO
46 DISCARD = _CLASS.DISCARD
47 REPLACE_XZ = _CLASS.REPLACE_XZ
Amin Hassani5ef5d452017-08-04 13:10:59 -070048 PUFFDIFF = _CLASS.PUFFDIFF
Amin Hassaniefa62d92017-11-09 13:46:56 -080049 BROTLI_BSDIFF = _CLASS.BROTLI_BSDIFF
Alex Deymo28466772015-09-11 17:16:44 -070050 ALL = (REPLACE, REPLACE_BZ, MOVE, BSDIFF, SOURCE_COPY, SOURCE_BSDIFF, ZERO,
Amin Hassaniefa62d92017-11-09 13:46:56 -080051 DISCARD, REPLACE_XZ, PUFFDIFF, BROTLI_BSDIFF)
Gilad Arnold553b0ec2013-01-26 01:00:39 -080052 NAMES = {
53 REPLACE: 'REPLACE',
54 REPLACE_BZ: 'REPLACE_BZ',
55 MOVE: 'MOVE',
56 BSDIFF: 'BSDIFF',
Allie Woodc11dc732015-02-18 15:53:05 -080057 SOURCE_COPY: 'SOURCE_COPY',
Alex Deymo28466772015-09-11 17:16:44 -070058 SOURCE_BSDIFF: 'SOURCE_BSDIFF',
59 ZERO: 'ZERO',
60 DISCARD: 'DISCARD',
Sen Jiangc2538fa2016-02-24 14:15:02 -080061 REPLACE_XZ: 'REPLACE_XZ',
Amin Hassani5ef5d452017-08-04 13:10:59 -070062 PUFFDIFF: 'PUFFDIFF',
Amin Hassaniefa62d92017-11-09 13:46:56 -080063 BROTLI_BSDIFF: 'BROTLI_BSDIFF',
Gilad Arnold553b0ec2013-01-26 01:00:39 -080064 }
65
66 def __init__(self):
67 pass
68
69
70#
Gilad Arnold382df5c2013-05-03 12:49:28 -070071# Checked and hashed reading of data.
Gilad Arnold553b0ec2013-01-26 01:00:39 -080072#
Gilad Arnold5502b562013-03-08 13:22:31 -080073def 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
Allie Wood12f59aa2015-04-06 11:05:12 -070079
Gilad Arnold5502b562013-03-08 13:22:31 -080080 Returns:
81 A format string for packing/unpacking integer values; assumes network byte
82 order (big-endian).
Allie Wood12f59aa2015-04-06 11:05:12 -070083
Gilad Arnold5502b562013-03-08 13:22:31 -080084 Raises:
85 PayloadError if something is wrong with the arguments.
Gilad Arnold5502b562013-03-08 13:22:31 -080086 """
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
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800107def 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)
Allie Wood12f59aa2015-04-06 11:05:12 -0700117
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800118 Returns:
119 A string containing the read data.
Allie Wood12f59aa2015-04-06 11:05:12 -0700120
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800121 Raises:
122 PayloadError if a read error occurred or not enough data was read.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800123 """
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."""
Gilad Arnolda7aa0bc2013-11-12 08:18:08 -0800159 return digest.encode('base64').strip()
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800160
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
Allie Wood12f59aa2015-04-06 11:05:12 -0700173
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800174 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).
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800177 """
178 idx, inc = (len(items), -1) if reverse else (1, 1)
Gilad Arnolde4beff72015-07-16 14:14:03 -0700179 if reverse:
180 items = reversed(items)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800181 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)