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