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