summaryrefslogtreecommitdiffstats
path: root/update-payload-extractor/update_payload/common.py
diff options
context:
space:
mode:
Diffstat (limited to 'update-payload-extractor/update_payload/common.py')
-rw-r--r--update-payload-extractor/update_payload/common.py218
1 files changed, 218 insertions, 0 deletions
diff --git a/update-payload-extractor/update_payload/common.py b/update-payload-extractor/update_payload/common.py
new file mode 100644
index 0000000..4e7b2e3
--- /dev/null
+++ b/update-payload-extractor/update_payload/common.py
@@ -0,0 +1,218 @@
+#
+# Copyright (C) 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""Utilities for update payload processing."""
+
+from __future__ import print_function
+
+from update_payload import update_metadata_pb2
+from update_payload.error import PayloadError
+
+
+#
+# Constants.
+#
+PSEUDO_EXTENT_MARKER = (1L << 64) - 1 # UINT64_MAX
+
+SIG_ASN1_HEADER = (
+ '\x30\x31\x30\x0d\x06\x09\x60\x86'
+ '\x48\x01\x65\x03\x04\x02\x01\x05'
+ '\x00\x04\x20'
+)
+
+CHROMEOS_MAJOR_PAYLOAD_VERSION = 1
+BRILLO_MAJOR_PAYLOAD_VERSION = 2
+
+INPLACE_MINOR_PAYLOAD_VERSION = 1
+SOURCE_MINOR_PAYLOAD_VERSION = 2
+OPSRCHASH_MINOR_PAYLOAD_VERSION = 3
+BROTLI_BSDIFF_MINOR_PAYLOAD_VERSION = 4
+PUFFDIFF_MINOR_PAYLOAD_VERSION = 5
+
+#
+# Payload operation types.
+#
+class OpType(object):
+ """Container for operation type constants."""
+ _CLASS = update_metadata_pb2.InstallOperation
+ REPLACE = _CLASS.REPLACE
+ REPLACE_BZ = _CLASS.REPLACE_BZ
+ MOVE = _CLASS.MOVE
+ BSDIFF = _CLASS.BSDIFF
+ SOURCE_COPY = _CLASS.SOURCE_COPY
+ SOURCE_BSDIFF = _CLASS.SOURCE_BSDIFF
+ ZERO = _CLASS.ZERO
+ DISCARD = _CLASS.DISCARD
+ REPLACE_XZ = _CLASS.REPLACE_XZ
+ PUFFDIFF = _CLASS.PUFFDIFF
+ BROTLI_BSDIFF = _CLASS.BROTLI_BSDIFF
+ ALL = (REPLACE, REPLACE_BZ, MOVE, BSDIFF, SOURCE_COPY, SOURCE_BSDIFF, ZERO,
+ DISCARD, REPLACE_XZ, PUFFDIFF, BROTLI_BSDIFF)
+ NAMES = {
+ REPLACE: 'REPLACE',
+ REPLACE_BZ: 'REPLACE_BZ',
+ MOVE: 'MOVE',
+ BSDIFF: 'BSDIFF',
+ SOURCE_COPY: 'SOURCE_COPY',
+ SOURCE_BSDIFF: 'SOURCE_BSDIFF',
+ ZERO: 'ZERO',
+ DISCARD: 'DISCARD',
+ REPLACE_XZ: 'REPLACE_XZ',
+ PUFFDIFF: 'PUFFDIFF',
+ BROTLI_BSDIFF: 'BROTLI_BSDIFF',
+ }
+
+ def __init__(self):
+ pass
+
+
+#
+# Checked and hashed reading of data.
+#
+def IntPackingFmtStr(size, is_unsigned):
+ """Returns an integer format string for use by the struct module.
+
+ Args:
+ size: the integer size in bytes (2, 4 or 8)
+ is_unsigned: whether it is signed or not
+
+ Returns:
+ A format string for packing/unpacking integer values; assumes network byte
+ order (big-endian).
+
+ Raises:
+ PayloadError if something is wrong with the arguments.
+ """
+ # Determine the base conversion format.
+ if size == 2:
+ fmt = 'h'
+ elif size == 4:
+ fmt = 'i'
+ elif size == 8:
+ fmt = 'q'
+ else:
+ raise PayloadError('unsupport numeric field size (%s)' % size)
+
+ # Signed or unsigned?
+ if is_unsigned:
+ fmt = fmt.upper()
+
+ # Make it network byte order (big-endian).
+ fmt = '!' + fmt
+
+ return fmt
+
+
+def Read(file_obj, length, offset=None, hasher=None):
+ """Reads binary data from a file.
+
+ Args:
+ file_obj: an open file object
+ length: the length of the data to read
+ offset: an offset to seek to prior to reading; this is an absolute offset
+ from either the beginning (non-negative) or end (negative) of the
+ file. (optional)
+ hasher: a hashing object to pass the read data through (optional)
+
+ Returns:
+ A string containing the read data.
+
+ Raises:
+ PayloadError if a read error occurred or not enough data was read.
+ """
+ if offset is not None:
+ if offset >= 0:
+ file_obj.seek(offset)
+ else:
+ file_obj.seek(offset, 2)
+
+ try:
+ data = file_obj.read(length)
+ except IOError, e:
+ raise PayloadError('error reading from file (%s): %s' % (file_obj.name, e))
+
+ if len(data) != length:
+ raise PayloadError(
+ 'reading from file (%s) too short (%d instead of %d bytes)' %
+ (file_obj.name, len(data), length))
+
+ if hasher:
+ hasher.update(data)
+
+ return data
+
+
+#
+# Formatting functions.
+#
+def FormatExtent(ex, block_size=0):
+ end_block = ex.start_block + ex.num_blocks
+ if block_size:
+ return '%d->%d * %d' % (ex.start_block, end_block, block_size)
+ else:
+ return '%d->%d' % (ex.start_block, end_block)
+
+
+def FormatSha256(digest):
+ """Returns a canonical string representation of a SHA256 digest."""
+ return digest.encode('base64').strip()
+
+
+#
+# Useful iterators.
+#
+def _ObjNameIter(items, base_name, reverse=False, name_format_func=None):
+ """A generic (item, name) tuple iterators.
+
+ Args:
+ items: the sequence of objects to iterate on
+ base_name: the base name for all objects
+ reverse: whether iteration should be in reverse order
+ name_format_func: a function to apply to the name string
+
+ Yields:
+ An iterator whose i-th invocation returns (items[i], name), where name ==
+ base_name + '[i]' (with a formatting function optionally applied to it).
+ """
+ idx, inc = (len(items), -1) if reverse else (1, 1)
+ if reverse:
+ items = reversed(items)
+ for item in items:
+ item_name = '%s[%d]' % (base_name, idx)
+ if name_format_func:
+ item_name = name_format_func(item, item_name)
+ yield (item, item_name)
+ idx += inc
+
+
+def _OperationNameFormatter(op, op_name):
+ return '%s(%s)' % (op_name, OpType.NAMES.get(op.type, '?'))
+
+
+def OperationIter(operations, base_name, reverse=False):
+ """An (item, name) iterator for update operations."""
+ return _ObjNameIter(operations, base_name, reverse=reverse,
+ name_format_func=_OperationNameFormatter)
+
+
+def ExtentIter(extents, base_name, reverse=False):
+ """An (item, name) iterator for operation extents."""
+ return _ObjNameIter(extents, base_name, reverse=reverse)
+
+
+def SignatureIter(sigs, base_name, reverse=False):
+ """An (item, name) iterator for signatures."""
+ return _ObjNameIter(sigs, base_name, reverse=reverse)