blob: 7c6ec8febe2be030e69f2b45e510ec4e891446d8 [file] [log] [blame]
Amin Hassanif94b6432018-01-26 17:39:47 -08001#
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#
Gilad Arnold553b0ec2013-01-26 01:00:39 -080016
17"""Utilities for update payload processing."""
18
Andrew Lassalle165843c2019-11-05 13:30:34 -080019from __future__ import absolute_import
Allie Wood12f59aa2015-04-06 11:05:12 -070020from __future__ import print_function
21
Andrew Lassalle165843c2019-11-05 13:30:34 -080022import base64
23
Amin Hassanib05a65a2017-12-18 15:15:32 -080024from update_payload import update_metadata_pb2
25from update_payload.error import PayloadError
Gilad Arnold553b0ec2013-01-26 01:00:39 -080026
27
28#
29# Constants.
30#
Gilad Arnold5502b562013-03-08 13:22:31 -080031SIG_ASN1_HEADER = (
Andrew Lassalle165843c2019-11-05 13:30:34 -080032 b'\x30\x31\x30\x0d\x06\x09\x60\x86'
33 b'\x48\x01\x65\x03\x04\x02\x01\x05'
34 b'\x00\x04\x20'
Gilad Arnold5502b562013-03-08 13:22:31 -080035)
36
Alex Deymoef497352015-10-15 09:14:58 -070037BRILLO_MAJOR_PAYLOAD_VERSION = 2
38
Allie Wood12f59aa2015-04-06 11:05:12 -070039SOURCE_MINOR_PAYLOAD_VERSION = 2
Sen Jiangd6122bb2015-12-11 10:27:04 -080040OPSRCHASH_MINOR_PAYLOAD_VERSION = 3
Amin Hassani77d7cbc2018-02-07 16:21:33 -080041BROTLI_BSDIFF_MINOR_PAYLOAD_VERSION = 4
42PUFFDIFF_MINOR_PAYLOAD_VERSION = 5
Gilad Arnold553b0ec2013-01-26 01:00:39 -080043
Tudor Brindus8d05a7e2018-06-14 11:18:18 -070044KERNEL = 'kernel'
Tudor Brindusb220d662018-07-10 23:55:51 -070045ROOTFS = 'root'
46# Tuple of (name in system, name in protobuf).
47CROS_PARTITIONS = ((KERNEL, KERNEL), (ROOTFS, 'rootfs'))
Tudor Brindus8d05a7e2018-06-14 11:18:18 -070048
Andrew Lassalle165843c2019-11-05 13:30:34 -080049
Gilad Arnold553b0ec2013-01-26 01:00:39 -080050#
51# Payload operation types.
52#
53class OpType(object):
54 """Container for operation type constants."""
Alex Deymo28466772015-09-11 17:16:44 -070055 _CLASS = update_metadata_pb2.InstallOperation
Gilad Arnold553b0ec2013-01-26 01:00:39 -080056 REPLACE = _CLASS.REPLACE
57 REPLACE_BZ = _CLASS.REPLACE_BZ
Allie Woodc11dc732015-02-18 15:53:05 -080058 SOURCE_COPY = _CLASS.SOURCE_COPY
59 SOURCE_BSDIFF = _CLASS.SOURCE_BSDIFF
Alex Deymo28466772015-09-11 17:16:44 -070060 ZERO = _CLASS.ZERO
61 DISCARD = _CLASS.DISCARD
62 REPLACE_XZ = _CLASS.REPLACE_XZ
Amin Hassani5ef5d452017-08-04 13:10:59 -070063 PUFFDIFF = _CLASS.PUFFDIFF
Amin Hassaniefa62d92017-11-09 13:46:56 -080064 BROTLI_BSDIFF = _CLASS.BROTLI_BSDIFF
Tianjiec7001692021-08-26 16:06:05 -070065 ZUCCHINI = _CLASS.ZUCCHINI
Amin Hassani0f59a9a2019-09-27 10:24:31 -070066 ALL = (REPLACE, REPLACE_BZ, SOURCE_COPY, SOURCE_BSDIFF, ZERO,
Tianjiec7001692021-08-26 16:06:05 -070067 DISCARD, REPLACE_XZ, PUFFDIFF, BROTLI_BSDIFF, ZUCCHINI)
Gilad Arnold553b0ec2013-01-26 01:00:39 -080068 NAMES = {
69 REPLACE: 'REPLACE',
70 REPLACE_BZ: 'REPLACE_BZ',
Allie Woodc11dc732015-02-18 15:53:05 -080071 SOURCE_COPY: 'SOURCE_COPY',
Alex Deymo28466772015-09-11 17:16:44 -070072 SOURCE_BSDIFF: 'SOURCE_BSDIFF',
73 ZERO: 'ZERO',
74 DISCARD: 'DISCARD',
Sen Jiangc2538fa2016-02-24 14:15:02 -080075 REPLACE_XZ: 'REPLACE_XZ',
Amin Hassani5ef5d452017-08-04 13:10:59 -070076 PUFFDIFF: 'PUFFDIFF',
Amin Hassaniefa62d92017-11-09 13:46:56 -080077 BROTLI_BSDIFF: 'BROTLI_BSDIFF',
Tianjiec7001692021-08-26 16:06:05 -070078 ZUCCHINI: 'ZUCCHINI',
Gilad Arnold553b0ec2013-01-26 01:00:39 -080079 }
80
81 def __init__(self):
82 pass
83
84
85#
Gilad Arnold382df5c2013-05-03 12:49:28 -070086# Checked and hashed reading of data.
Gilad Arnold553b0ec2013-01-26 01:00:39 -080087#
Gilad Arnold5502b562013-03-08 13:22:31 -080088def 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
Allie Wood12f59aa2015-04-06 11:05:12 -070094
Gilad Arnold5502b562013-03-08 13:22:31 -080095 Returns:
96 A format string for packing/unpacking integer values; assumes network byte
97 order (big-endian).
Allie Wood12f59aa2015-04-06 11:05:12 -070098
Gilad Arnold5502b562013-03-08 13:22:31 -080099 Raises:
100 PayloadError if something is wrong with the arguments.
Gilad Arnold5502b562013-03-08 13:22:31 -0800101 """
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
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800122def 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)
Allie Wood12f59aa2015-04-06 11:05:12 -0700132
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800133 Returns:
134 A string containing the read data.
Allie Wood12f59aa2015-04-06 11:05:12 -0700135
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800136 Raises:
137 PayloadError if a read error occurred or not enough data was read.
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800138 """
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)
Andrew Lassalle165843c2019-11-05 13:30:34 -0800147 except IOError as e:
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800148 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)
Amin Hassani55c75412019-10-07 11:20:39 -0700168 return '%d->%d' % (ex.start_block, end_block)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800169
170
171def FormatSha256(digest):
172 """Returns a canonical string representation of a SHA256 digest."""
Andrew Lassalle165843c2019-11-05 13:30:34 -0800173 return base64.b64encode(digest).decode('utf-8')
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800174
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
Allie Wood12f59aa2015-04-06 11:05:12 -0700187
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800188 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).
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800191 """
192 idx, inc = (len(items), -1) if reverse else (1, 1)
Gilad Arnolde4beff72015-07-16 14:14:03 -0700193 if reverse:
194 items = reversed(items)
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800195 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)