blob: 16509911fdaea6f7cea198e4c372fb75b07d6c75 [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
8import textwrap
9
10from error import PayloadError
11import update_metadata_pb2
12
13
14#
15# Constants.
16#
17PSEUDO_EXTENT_MARKER = ctypes.c_uint64(-1).value
18
19
20#
21# Payload operation types.
22#
23class OpType(object):
24 """Container for operation type constants."""
25 _CLASS = update_metadata_pb2.DeltaArchiveManifest.InstallOperation
26 # pylint: disable=E1101
27 REPLACE = _CLASS.REPLACE
28 REPLACE_BZ = _CLASS.REPLACE_BZ
29 MOVE = _CLASS.MOVE
30 BSDIFF = _CLASS.BSDIFF
31 NAMES = {
32 REPLACE: 'REPLACE',
33 REPLACE_BZ: 'REPLACE_BZ',
34 MOVE: 'MOVE',
35 BSDIFF: 'BSDIFF',
36 }
37
38 def __init__(self):
39 pass
40
41
42#
43# Checker and hashed reading of data.
44#
45def Read(file_obj, length, offset=None, hasher=None):
46 """Reads binary data from a file.
47
48 Args:
49 file_obj: an open file object
50 length: the length of the data to read
51 offset: an offset to seek to prior to reading; this is an absolute offset
52 from either the beginning (non-negative) or end (negative) of the
53 file. (optional)
54 hasher: a hashing object to pass the read data through (optional)
55 Returns:
56 A string containing the read data.
57 Raises:
58 PayloadError if a read error occurred or not enough data was read.
59
60 """
61 if offset is not None:
62 if offset >= 0:
63 file_obj.seek(offset)
64 else:
65 file_obj.seek(offset, 2)
66
67 try:
68 data = file_obj.read(length)
69 except IOError, e:
70 raise PayloadError('error reading from file (%s): %s' % (file_obj.name, e))
71
72 if len(data) != length:
73 raise PayloadError(
74 'reading from file (%s) too short (%d instead of %d bytes)' %
75 (file_obj.name, len(data), length))
76
77 if hasher:
78 hasher.update(data)
79
80 return data
81
82
83#
84# Formatting functions.
85#
86def FormatExtent(ex, block_size=0):
87 end_block = ex.start_block + ex.num_blocks
88 if block_size:
89 return '%d->%d * %d' % (ex.start_block, end_block, block_size)
90 else:
91 return '%d->%d' % (ex.start_block, end_block)
92
93
94def FormatSha256(digest):
95 """Returns a canonical string representation of a SHA256 digest."""
96 return '\n'.join(textwrap.wrap(digest.encode('hex'), 32))
97
98
99#
100# Useful iterators.
101#
102def _ObjNameIter(items, base_name, reverse=False, name_format_func=None):
103 """A generic (item, name) tuple iterators.
104
105 Args:
106 items: the sequence of objects to iterate on
107 base_name: the base name for all objects
108 reverse: whether iteration should be in reverse order
109 name_format_func: a function to apply to the name string
110 Yields:
111 An iterator whose i-th invocation returns (items[i], name), where name ==
112 base_name + '[i]' (with a formatting function optionally applied to it).
113
114 """
115 idx, inc = (len(items), -1) if reverse else (1, 1)
116 for item in items:
117 item_name = '%s[%d]' % (base_name, idx)
118 if name_format_func:
119 item_name = name_format_func(item, item_name)
120 yield (item, item_name)
121 idx += inc
122
123
124def _OperationNameFormatter(op, op_name):
125 return '%s(%s)' % (op_name, OpType.NAMES.get(op.type, '?'))
126
127
128def OperationIter(operations, base_name, reverse=False):
129 """An (item, name) iterator for update operations."""
130 return _ObjNameIter(operations, base_name, reverse=reverse,
131 name_format_func=_OperationNameFormatter)
132
133
134def ExtentIter(extents, base_name, reverse=False):
135 """An (item, name) iterator for operation extents."""
136 return _ObjNameIter(extents, base_name, reverse=reverse)
137
138
139def SignatureIter(sigs, base_name, reverse=False):
140 """An (item, name) iterator for signatures."""
141 return _ObjNameIter(sigs, base_name, reverse=reverse)