blob: 573fda406e006537e7530d3f16470a636213b432 [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 -08009import ctypes
Gilad Arnold553b0ec2013-01-26 01:00:39 -080010
11from error import PayloadError
12import update_metadata_pb2
13
14
15#
16# Constants.
17#
18PSEUDO_EXTENT_MARKER = ctypes.c_uint64(-1).value
19
Gilad Arnold5502b562013-03-08 13:22:31 -080020SIG_ASN1_HEADER = (
21 '\x30\x31\x30\x0d\x06\x09\x60\x86'
22 '\x48\x01\x65\x03\x04\x02\x01\x05'
23 '\x00\x04\x20'
24)
25
Allie Wood12f59aa2015-04-06 11:05:12 -070026INPLACE_MINOR_PAYLOAD_VERSION = 1
27SOURCE_MINOR_PAYLOAD_VERSION = 2
Gilad Arnold553b0ec2013-01-26 01:00:39 -080028
29#
30# Payload operation types.
31#
32class OpType(object):
33 """Container for operation type constants."""
34 _CLASS = update_metadata_pb2.DeltaArchiveManifest.InstallOperation
35 # pylint: disable=E1101
36 REPLACE = _CLASS.REPLACE
37 REPLACE_BZ = _CLASS.REPLACE_BZ
38 MOVE = _CLASS.MOVE
39 BSDIFF = _CLASS.BSDIFF
Allie Woodc11dc732015-02-18 15:53:05 -080040 SOURCE_COPY = _CLASS.SOURCE_COPY
41 SOURCE_BSDIFF = _CLASS.SOURCE_BSDIFF
42 ALL = (REPLACE, REPLACE_BZ, MOVE, BSDIFF, SOURCE_COPY, SOURCE_BSDIFF)
Gilad Arnold553b0ec2013-01-26 01:00:39 -080043 NAMES = {
44 REPLACE: 'REPLACE',
45 REPLACE_BZ: 'REPLACE_BZ',
46 MOVE: 'MOVE',
47 BSDIFF: 'BSDIFF',
Allie Woodc11dc732015-02-18 15:53:05 -080048 SOURCE_COPY: 'SOURCE_COPY',
49 SOURCE_BSDIFF: 'SOURCE_BSDIFF'
Gilad Arnold553b0ec2013-01-26 01:00:39 -080050 }
51
52 def __init__(self):
53 pass
54
55
56#
Gilad Arnold382df5c2013-05-03 12:49:28 -070057# Checked and hashed reading of data.
Gilad Arnold553b0ec2013-01-26 01:00:39 -080058#
Gilad Arnold5502b562013-03-08 13:22:31 -080059def IntPackingFmtStr(size, is_unsigned):
60 """Returns an integer format string for use by the struct module.
61
62 Args:
63 size: the integer size in bytes (2, 4 or 8)
64 is_unsigned: whether it is signed or not
Allie Wood12f59aa2015-04-06 11:05:12 -070065
Gilad Arnold5502b562013-03-08 13:22:31 -080066 Returns:
67 A format string for packing/unpacking integer values; assumes network byte
68 order (big-endian).
Allie Wood12f59aa2015-04-06 11:05:12 -070069
Gilad Arnold5502b562013-03-08 13:22:31 -080070 Raises:
71 PayloadError if something is wrong with the arguments.
72
73 """
74 # Determine the base conversion format.
75 if size == 2:
76 fmt = 'h'
77 elif size == 4:
78 fmt = 'i'
79 elif size == 8:
80 fmt = 'q'
81 else:
82 raise PayloadError('unsupport numeric field size (%s)' % size)
83
84 # Signed or unsigned?
85 if is_unsigned:
86 fmt = fmt.upper()
87
88 # Make it network byte order (big-endian).
89 fmt = '!' + fmt
90
91 return fmt
92
93
Gilad Arnold553b0ec2013-01-26 01:00:39 -080094def Read(file_obj, length, offset=None, hasher=None):
95 """Reads binary data from a file.
96
97 Args:
98 file_obj: an open file object
99 length: the length of the data to read
100 offset: an offset to seek to prior to reading; this is an absolute offset
101 from either the beginning (non-negative) or end (negative) of the
102 file. (optional)
103 hasher: a hashing object to pass the read data through (optional)
Allie Wood12f59aa2015-04-06 11:05:12 -0700104
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800105 Returns:
106 A string containing the read data.
Allie Wood12f59aa2015-04-06 11:05:12 -0700107
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800108 Raises:
109 PayloadError if a read error occurred or not enough data was read.
110
111 """
112 if offset is not None:
113 if offset >= 0:
114 file_obj.seek(offset)
115 else:
116 file_obj.seek(offset, 2)
117
118 try:
119 data = file_obj.read(length)
120 except IOError, e:
121 raise PayloadError('error reading from file (%s): %s' % (file_obj.name, e))
122
123 if len(data) != length:
124 raise PayloadError(
125 'reading from file (%s) too short (%d instead of %d bytes)' %
126 (file_obj.name, len(data), length))
127
128 if hasher:
129 hasher.update(data)
130
131 return data
132
133
134#
135# Formatting functions.
136#
137def FormatExtent(ex, block_size=0):
138 end_block = ex.start_block + ex.num_blocks
139 if block_size:
140 return '%d->%d * %d' % (ex.start_block, end_block, block_size)
141 else:
142 return '%d->%d' % (ex.start_block, end_block)
143
144
145def FormatSha256(digest):
146 """Returns a canonical string representation of a SHA256 digest."""
Gilad Arnolda7aa0bc2013-11-12 08:18:08 -0800147 return digest.encode('base64').strip()
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800148
149
150#
151# Useful iterators.
152#
153def _ObjNameIter(items, base_name, reverse=False, name_format_func=None):
154 """A generic (item, name) tuple iterators.
155
156 Args:
157 items: the sequence of objects to iterate on
158 base_name: the base name for all objects
159 reverse: whether iteration should be in reverse order
160 name_format_func: a function to apply to the name string
Allie Wood12f59aa2015-04-06 11:05:12 -0700161
Gilad Arnold553b0ec2013-01-26 01:00:39 -0800162 Yields:
163 An iterator whose i-th invocation returns (items[i], name), where name ==
164 base_name + '[i]' (with a formatting function optionally applied to it).
165
166 """
167 idx, inc = (len(items), -1) if reverse else (1, 1)
168 for item in items:
169 item_name = '%s[%d]' % (base_name, idx)
170 if name_format_func:
171 item_name = name_format_func(item, item_name)
172 yield (item, item_name)
173 idx += inc
174
175
176def _OperationNameFormatter(op, op_name):
177 return '%s(%s)' % (op_name, OpType.NAMES.get(op.type, '?'))
178
179
180def OperationIter(operations, base_name, reverse=False):
181 """An (item, name) iterator for update operations."""
182 return _ObjNameIter(operations, base_name, reverse=reverse,
183 name_format_func=_OperationNameFormatter)
184
185
186def ExtentIter(extents, base_name, reverse=False):
187 """An (item, name) iterator for operation extents."""
188 return _ObjNameIter(extents, base_name, reverse=reverse)
189
190
191def SignatureIter(sigs, base_name, reverse=False):
192 """An (item, name) iterator for signatures."""
193 return _ObjNameIter(sigs, base_name, reverse=reverse)