1# 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# 16 17"""Utilities for update payload processing.""" 18 19from __future__ import print_function 20 21from update_payload import update_metadata_pb2 22from update_payload.error import PayloadError 23 24 25# 26# Constants. 27# 28PSEUDO_EXTENT_MARKER = (1L << 64) - 1 # UINT64_MAX 29 30SIG_ASN1_HEADER = ( 31 '\x30\x31\x30\x0d\x06\x09\x60\x86' 32 '\x48\x01\x65\x03\x04\x02\x01\x05' 33 '\x00\x04\x20' 34) 35 36CHROMEOS_MAJOR_PAYLOAD_VERSION = 1 37BRILLO_MAJOR_PAYLOAD_VERSION = 2 38 39INPLACE_MINOR_PAYLOAD_VERSION = 1 40SOURCE_MINOR_PAYLOAD_VERSION = 2 41OPSRCHASH_MINOR_PAYLOAD_VERSION = 3 42BROTLI_BSDIFF_MINOR_PAYLOAD_VERSION = 4 43PUFFDIFF_MINOR_PAYLOAD_VERSION = 5 44 45KERNEL = 'kernel' 46ROOTFS = 'root' 47# Tuple of (name in system, name in protobuf). 48CROS_PARTITIONS = ((KERNEL, KERNEL), (ROOTFS, 'rootfs')) 49 50# 51# Payload operation types. 52# 53class OpType(object): 54 """Container for operation type constants.""" 55 _CLASS = update_metadata_pb2.InstallOperation 56 REPLACE = _CLASS.REPLACE 57 REPLACE_BZ = _CLASS.REPLACE_BZ 58 MOVE = _CLASS.MOVE 59 BSDIFF = _CLASS.BSDIFF 60 SOURCE_COPY = _CLASS.SOURCE_COPY 61 SOURCE_BSDIFF = _CLASS.SOURCE_BSDIFF 62 ZERO = _CLASS.ZERO 63 DISCARD = _CLASS.DISCARD 64 REPLACE_XZ = _CLASS.REPLACE_XZ 65 PUFFDIFF = _CLASS.PUFFDIFF 66 BROTLI_BSDIFF = _CLASS.BROTLI_BSDIFF 67 ALL = (REPLACE, REPLACE_BZ, MOVE, BSDIFF, SOURCE_COPY, SOURCE_BSDIFF, ZERO, 68 DISCARD, REPLACE_XZ, PUFFDIFF, BROTLI_BSDIFF) 69 NAMES = { 70 REPLACE: 'REPLACE', 71 REPLACE_BZ: 'REPLACE_BZ', 72 MOVE: 'MOVE', 73 BSDIFF: 'BSDIFF', 74 SOURCE_COPY: 'SOURCE_COPY', 75 SOURCE_BSDIFF: 'SOURCE_BSDIFF', 76 ZERO: 'ZERO', 77 DISCARD: 'DISCARD', 78 REPLACE_XZ: 'REPLACE_XZ', 79 PUFFDIFF: 'PUFFDIFF', 80 BROTLI_BSDIFF: 'BROTLI_BSDIFF', 81 } 82 83 def __init__(self): 84 pass 85 86 87# 88# Checked and hashed reading of data. 89# 90def IntPackingFmtStr(size, is_unsigned): 91 """Returns an integer format string for use by the struct module. 92 93 Args: 94 size: the integer size in bytes (2, 4 or 8) 95 is_unsigned: whether it is signed or not 96 97 Returns: 98 A format string for packing/unpacking integer values; assumes network byte 99 order (big-endian). 100 101 Raises: 102 PayloadError if something is wrong with the arguments. 103 """ 104 # Determine the base conversion format. 105 if size == 2: 106 fmt = 'h' 107 elif size == 4: 108 fmt = 'i' 109 elif size == 8: 110 fmt = 'q' 111 else: 112 raise PayloadError('unsupport numeric field size (%s)' % size) 113 114 # Signed or unsigned? 115 if is_unsigned: 116 fmt = fmt.upper() 117 118 # Make it network byte order (big-endian). 119 fmt = '!' + fmt 120 121 return fmt 122 123 124def Read(file_obj, length, offset=None, hasher=None): 125 """Reads binary data from a file. 126 127 Args: 128 file_obj: an open file object 129 length: the length of the data to read 130 offset: an offset to seek to prior to reading; this is an absolute offset 131 from either the beginning (non-negative) or end (negative) of the 132 file. (optional) 133 hasher: a hashing object to pass the read data through (optional) 134 135 Returns: 136 A string containing the read data. 137 138 Raises: 139 PayloadError if a read error occurred or not enough data was read. 140 """ 141 if offset is not None: 142 if offset >= 0: 143 file_obj.seek(offset) 144 else: 145 file_obj.seek(offset, 2) 146 147 try: 148 data = file_obj.read(length) 149 except IOError, e: 150 raise PayloadError('error reading from file (%s): %s' % (file_obj.name, e)) 151 152 if len(data) != length: 153 raise PayloadError( 154 'reading from file (%s) too short (%d instead of %d bytes)' % 155 (file_obj.name, len(data), length)) 156 157 if hasher: 158 hasher.update(data) 159 160 return data 161 162 163# 164# Formatting functions. 165# 166def FormatExtent(ex, block_size=0): 167 end_block = ex.start_block + ex.num_blocks 168 if block_size: 169 return '%d->%d * %d' % (ex.start_block, end_block, block_size) 170 else: 171 return '%d->%d' % (ex.start_block, end_block) 172 173 174def FormatSha256(digest): 175 """Returns a canonical string representation of a SHA256 digest.""" 176 return digest.encode('base64').strip() 177 178 179# 180# Useful iterators. 181# 182def _ObjNameIter(items, base_name, reverse=False, name_format_func=None): 183 """A generic (item, name) tuple iterators. 184 185 Args: 186 items: the sequence of objects to iterate on 187 base_name: the base name for all objects 188 reverse: whether iteration should be in reverse order 189 name_format_func: a function to apply to the name string 190 191 Yields: 192 An iterator whose i-th invocation returns (items[i], name), where name == 193 base_name + '[i]' (with a formatting function optionally applied to it). 194 """ 195 idx, inc = (len(items), -1) if reverse else (1, 1) 196 if reverse: 197 items = reversed(items) 198 for item in items: 199 item_name = '%s[%d]' % (base_name, idx) 200 if name_format_func: 201 item_name = name_format_func(item, item_name) 202 yield (item, item_name) 203 idx += inc 204 205 206def _OperationNameFormatter(op, op_name): 207 return '%s(%s)' % (op_name, OpType.NAMES.get(op.type, '?')) 208 209 210def OperationIter(operations, base_name, reverse=False): 211 """An (item, name) iterator for update operations.""" 212 return _ObjNameIter(operations, base_name, reverse=reverse, 213 name_format_func=_OperationNameFormatter) 214 215 216def ExtentIter(extents, base_name, reverse=False): 217 """An (item, name) iterator for operation extents.""" 218 return _ObjNameIter(extents, base_name, reverse=reverse) 219 220 221def SignatureIter(sigs, base_name, reverse=False): 222 """An (item, name) iterator for signatures.""" 223 return _ObjNameIter(sigs, base_name, reverse=reverse) 224