blob: e8c7c766cbcedc5825f07bf9064ae2d14533fb9a [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
Gilad Arnold553b0ec2013-01-26 01:00:39 -08009from error import PayloadError
10import update_metadata_pb2
11
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
Allie Wood12f59aa2015-04-06 11:05:12 -070024INPLACE_MINOR_PAYLOAD_VERSION = 1
25SOURCE_MINOR_PAYLOAD_VERSION = 2
Gilad Arnold553b0ec2013-01-26 01:00:39 -080026
27#
28# Payload operation types.
29#
30class OpType(object):
31 """Container for operation type constants."""
32 _CLASS = update_metadata_pb2.DeltaArchiveManifest.InstallOperation
33 # pylint: disable=E1101
34 REPLACE = _CLASS.REPLACE
35 REPLACE_BZ = _CLASS.REPLACE_BZ
36 MOVE = _CLASS.MOVE
37 BSDIFF = _CLASS.BSDIFF
Allie Woodc11dc732015-02-18 15:53:05 -080038 SOURCE_COPY = _CLASS.SOURCE_COPY
39 SOURCE_BSDIFF = _CLASS.SOURCE_BSDIFF
40 ALL = (REPLACE, REPLACE_BZ, MOVE, BSDIFF, SOURCE_COPY, SOURCE_BSDIFF)
Gilad Arnold553b0ec2013-01-26 01:00:39 -080041 NAMES = {
42 REPLACE: 'REPLACE',
43 REPLACE_BZ: 'REPLACE_BZ',
44 MOVE: 'MOVE',
45 BSDIFF: 'BSDIFF',
Allie Woodc11dc732015-02-18 15:53:05 -080046 SOURCE_COPY: 'SOURCE_COPY',
47 SOURCE_BSDIFF: 'SOURCE_BSDIFF'
Gilad Arnold553b0ec2013-01-26 01:00:39 -080048 }
49
50 def __init__(self):
51 pass
52
53
54#
Gilad Arnold382df5c2013-05-03 12:49:28 -070055# Checked and hashed reading of data.
Gilad Arnold553b0ec2013-01-26 01:00:39 -080056#
Gilad Arnold5502b562013-03-08 13:22:31 -080057def IntPackingFmtStr(size, is_unsigned):
58 """Returns an integer format string for use by the struct module.
59
60 Args:
61 size: the integer size in bytes (2, 4 or 8)
62 is_unsigned: whether it is signed or not
Allie Wood12f59aa2015-04-06 11:05:12 -070063
Gilad Arnold5502b562013-03-08 13:22:31 -080064 Returns:
65 A format string for packing/unpacking integer values; assumes network byte
66 order (big-endian).
Allie Wood12f59aa2015-04-06 11:05:12 -070067
Gilad Arnold5502b562013-03-08 13:22:31 -080068 Raises:
69 PayloadError if something is wrong with the arguments.
70
71 """
72 # Determine the base conversion format.
73 if size == 2:
74 fmt = 'h'
75 elif size == 4:
76 fmt = 'i'
77 elif size == 8:
78 fmt = 'q'
79 else:
80 raise PayloadError('unsupport numeric field size (%s)' % size)
81
82 # Signed or unsigned?
83 if is_unsigned:
84 fmt = fmt.upper()
85
86 # Make it network byte order (big-endian).
87 fmt = '!' + fmt
88
89 return fmt
90
91
Gilad Arnold553b0ec2013-01-26 01:00:39 -080092def Read(file_obj, length, offset=None, hasher=None):
93 """Reads binary data from a file.
94
95 Args:
96 file_obj: an open file object
97 length: the length of the data to read
98 offset: an offset to seek to prior to reading; this is an absolute offset
99 from either the beginning (non-negative) or end (negative) of the
100 file. (optional)
101 hasher: a hashing object to pass the read data through (optional)
Allie Wood12f59aa2015-04-06 11:05:12 -0700102
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800103 Returns:
104 A string containing the read data.
Allie Wood12f59aa2015-04-06 11:05:12 -0700105
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800106 Raises:
107 PayloadError if a read error occurred or not enough data was read.
108
109 """
110 if offset is not None:
111 if offset >= 0:
112 file_obj.seek(offset)
113 else:
114 file_obj.seek(offset, 2)
115
116 try:
117 data = file_obj.read(length)
118 except IOError, e:
119 raise PayloadError('error reading from file (%s): %s' % (file_obj.name, e))
120
121 if len(data) != length:
122 raise PayloadError(
123 'reading from file (%s) too short (%d instead of %d bytes)' %
124 (file_obj.name, len(data), length))
125
126 if hasher:
127 hasher.update(data)
128
129 return data
130
131
132#
133# Formatting functions.
134#
135def FormatExtent(ex, block_size=0):
136 end_block = ex.start_block + ex.num_blocks
137 if block_size:
138 return '%d->%d * %d' % (ex.start_block, end_block, block_size)
139 else:
140 return '%d->%d' % (ex.start_block, end_block)
141
142
143def FormatSha256(digest):
144 """Returns a canonical string representation of a SHA256 digest."""
Gilad Arnolda7aa0bc2013-11-12 08:18:08 -0800145 return digest.encode('base64').strip()
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800146
147
148#
149# Useful iterators.
150#
151def _ObjNameIter(items, base_name, reverse=False, name_format_func=None):
152 """A generic (item, name) tuple iterators.
153
154 Args:
155 items: the sequence of objects to iterate on
156 base_name: the base name for all objects
157 reverse: whether iteration should be in reverse order
158 name_format_func: a function to apply to the name string
Allie Wood12f59aa2015-04-06 11:05:12 -0700159
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800160 Yields:
161 An iterator whose i-th invocation returns (items[i], name), where name ==
162 base_name + '[i]' (with a formatting function optionally applied to it).
163
164 """
165 idx, inc = (len(items), -1) if reverse else (1, 1)
166 for item in items:
167 item_name = '%s[%d]' % (base_name, idx)
168 if name_format_func:
169 item_name = name_format_func(item, item_name)
170 yield (item, item_name)
171 idx += inc
172
173
174def _OperationNameFormatter(op, op_name):
175 return '%s(%s)' % (op_name, OpType.NAMES.get(op.type, '?'))
176
177
178def OperationIter(operations, base_name, reverse=False):
179 """An (item, name) iterator for update operations."""
180 return _ObjNameIter(operations, base_name, reverse=reverse,
181 name_format_func=_OperationNameFormatter)
182
183
184def ExtentIter(extents, base_name, reverse=False):
185 """An (item, name) iterator for operation extents."""
186 return _ObjNameIter(extents, base_name, reverse=reverse)
187
188
189def SignatureIter(sigs, base_name, reverse=False):
190 """An (item, name) iterator for signatures."""
191 return _ObjNameIter(sigs, base_name, reverse=reverse)