common.py 5.9 KB

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