msgpack.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. from .datastruct import StableDict
  2. from ..constants import * # NOQA
  3. # wrapping msgpack ---------------------------------------------------------------------------------------------------
  4. #
  5. # due to the planned breaking api changes in upstream msgpack, we wrap it the way we need it -
  6. # to avoid having lots of clutter in the calling code. see tickets #968 and #3632.
  7. #
  8. # Packing
  9. # -------
  10. # use_bin_type = False is needed to generate the old msgpack format (not msgpack 2.0 spec) as borg always did.
  11. # unicode_errors = None is needed because usage of it is deprecated
  12. #
  13. # Unpacking
  14. # ---------
  15. # raw = True is needed to unpack the old msgpack format to bytes (not str, about the decoding see item.pyx).
  16. # unicode_errors = None is needed because usage of it is deprecated
  17. from msgpack import Packer as mp_Packer
  18. from msgpack import packb as mp_packb
  19. from msgpack import pack as mp_pack
  20. from msgpack import Unpacker as mp_Unpacker
  21. from msgpack import unpackb as mp_unpackb
  22. from msgpack import unpack as mp_unpack
  23. from msgpack import version as mp_version
  24. from msgpack import ExtType
  25. from msgpack import OutOfData
  26. version = mp_version
  27. _post_100 = version >= (1, 0, 0)
  28. class PackException(Exception):
  29. """Exception while msgpack packing"""
  30. class UnpackException(Exception):
  31. """Exception while msgpack unpacking"""
  32. class Packer(mp_Packer):
  33. def __init__(self, *, default=None, unicode_errors=None,
  34. use_single_float=False, autoreset=True, use_bin_type=False,
  35. strict_types=False):
  36. assert unicode_errors is None
  37. super().__init__(default=default, unicode_errors=unicode_errors,
  38. use_single_float=use_single_float, autoreset=autoreset, use_bin_type=use_bin_type,
  39. strict_types=strict_types)
  40. def pack(self, obj):
  41. try:
  42. return super().pack(obj)
  43. except Exception as e:
  44. raise PackException(e)
  45. def packb(o, *, use_bin_type=False, unicode_errors=None, **kwargs):
  46. assert unicode_errors is None
  47. try:
  48. return mp_packb(o, use_bin_type=use_bin_type, unicode_errors=unicode_errors, **kwargs)
  49. except Exception as e:
  50. raise PackException(e)
  51. def pack(o, stream, *, use_bin_type=False, unicode_errors=None, **kwargs):
  52. assert unicode_errors is None
  53. try:
  54. return mp_pack(o, stream, use_bin_type=use_bin_type, unicode_errors=unicode_errors, **kwargs)
  55. except Exception as e:
  56. raise PackException(e)
  57. # Note: after requiring msgpack >= 0.6.1 we can remove the max_*_len args and
  58. # rely on msgpack auto-computing DoS-safe max values from len(data) for
  59. # unpack(data) or from max_buffer_len for Unpacker(max_buffer_len=N).
  60. # maybe we can also use that to simplify get_limited_unpacker().
  61. class Unpacker(mp_Unpacker):
  62. def __init__(self, file_like=None, *, read_size=0, use_list=True, raw=True,
  63. object_hook=None, object_pairs_hook=None, list_hook=None,
  64. unicode_errors=None, max_buffer_size=0,
  65. ext_hook=ExtType,
  66. strict_map_key=False,
  67. max_str_len=2147483647, # 2**32-1
  68. max_bin_len=2147483647,
  69. max_array_len=2147483647,
  70. max_map_len=2147483647,
  71. max_ext_len=2147483647):
  72. assert raw is True
  73. assert unicode_errors is None
  74. kw = dict(file_like=file_like, read_size=read_size, use_list=use_list, raw=raw,
  75. object_hook=object_hook, object_pairs_hook=object_pairs_hook, list_hook=list_hook,
  76. unicode_errors=unicode_errors, max_buffer_size=max_buffer_size,
  77. ext_hook=ext_hook,
  78. max_str_len=max_str_len,
  79. max_bin_len=max_bin_len,
  80. max_array_len=max_array_len,
  81. max_map_len=max_map_len,
  82. max_ext_len=max_ext_len)
  83. if _post_100:
  84. kw['strict_map_key'] = strict_map_key
  85. super().__init__(**kw)
  86. def unpack(self):
  87. try:
  88. return super().unpack()
  89. except OutOfData:
  90. raise
  91. except Exception as e:
  92. raise UnpackException(e)
  93. def __next__(self):
  94. try:
  95. return super().__next__()
  96. except StopIteration:
  97. raise
  98. except Exception as e:
  99. raise UnpackException(e)
  100. next = __next__
  101. def unpackb(packed, *, raw=True, unicode_errors=None,
  102. strict_map_key=False,
  103. max_str_len=2147483647, # 2**32-1
  104. max_bin_len=2147483647,
  105. max_array_len=2147483647,
  106. max_map_len=2147483647,
  107. max_ext_len=2147483647,
  108. **kwargs):
  109. assert unicode_errors is None
  110. try:
  111. kw = dict(raw=raw, unicode_errors=unicode_errors,
  112. max_str_len=max_str_len,
  113. max_bin_len=max_bin_len,
  114. max_array_len=max_array_len,
  115. max_map_len=max_map_len,
  116. max_ext_len=max_ext_len)
  117. kw.update(kwargs)
  118. if _post_100:
  119. kw['strict_map_key'] = strict_map_key
  120. return mp_unpackb(packed, **kw)
  121. except Exception as e:
  122. raise UnpackException(e)
  123. def unpack(stream, *, raw=True, unicode_errors=None,
  124. strict_map_key=False,
  125. max_str_len=2147483647, # 2**32-1
  126. max_bin_len=2147483647,
  127. max_array_len=2147483647,
  128. max_map_len=2147483647,
  129. max_ext_len=2147483647,
  130. **kwargs):
  131. assert unicode_errors is None
  132. try:
  133. kw = dict(raw=raw, unicode_errors=unicode_errors,
  134. max_str_len=max_str_len,
  135. max_bin_len=max_bin_len,
  136. max_array_len=max_array_len,
  137. max_map_len=max_map_len,
  138. max_ext_len=max_ext_len)
  139. kw.update(kwargs)
  140. if _post_100:
  141. kw['strict_map_key'] = strict_map_key
  142. return mp_unpack(stream, **kw)
  143. except Exception as e:
  144. raise UnpackException(e)
  145. # msgpacking related utilities -----------------------------------------------
  146. def is_slow_msgpack():
  147. import msgpack
  148. import msgpack.fallback
  149. return msgpack.Packer is msgpack.fallback.Packer
  150. def is_supported_msgpack():
  151. # DO NOT CHANGE OR REMOVE! See also requirements and comments in setup.py.
  152. import msgpack
  153. return (0, 5, 6) <= msgpack.version <= (1, 0, 8) and \
  154. msgpack.version not in [(1, 0, 1), ] # < add bad releases here to deny list
  155. def get_limited_unpacker(kind):
  156. """return a limited Unpacker because we should not trust msgpack data received from remote"""
  157. args = dict(use_list=False, # return tuples, not lists
  158. max_bin_len=0, # not used
  159. max_ext_len=0, # not used
  160. max_buffer_size=3 * max(BUFSIZE, MAX_OBJECT_SIZE),
  161. max_str_len=MAX_OBJECT_SIZE, # a chunk or other repo object
  162. )
  163. if kind == 'server':
  164. args.update(dict(max_array_len=100, # misc. cmd tuples
  165. max_map_len=100, # misc. cmd dicts
  166. ))
  167. elif kind == 'client':
  168. args.update(dict(max_array_len=LIST_SCAN_LIMIT, # result list from repo.list() / .scan()
  169. max_map_len=100, # misc. result dicts
  170. ))
  171. elif kind == 'manifest':
  172. args.update(dict(use_list=True, # default value
  173. max_array_len=100, # ITEM_KEYS ~= 22
  174. max_map_len=MAX_ARCHIVES, # list of archives
  175. max_str_len=255, # archive name
  176. object_hook=StableDict,
  177. ))
  178. elif kind == 'archive':
  179. args.update(dict(use_list=True, # default value
  180. max_map_len=100, # ARCHIVE_KEYS ~= 20
  181. max_str_len=10000, # comment
  182. object_hook=StableDict,
  183. ))
  184. elif kind == 'key':
  185. args.update(dict(use_list=True, # default value
  186. max_array_len=0, # not used
  187. max_map_len=10, # EncryptedKey dict
  188. max_str_len=4000, # inner key data
  189. object_hook=StableDict,
  190. ))
  191. else:
  192. raise ValueError('kind must be "server", "client", "manifest" or "key"')
  193. return Unpacker(**args)
  194. def bigint_to_int(mtime):
  195. """Convert bytearray to int
  196. """
  197. if isinstance(mtime, bytes):
  198. return int.from_bytes(mtime, 'little', signed=True)
  199. return mtime
  200. def int_to_bigint(value):
  201. """Convert integers larger than 64 bits to bytearray
  202. Smaller integers are left alone
  203. """
  204. if value.bit_length() > 63:
  205. return value.to_bytes((value.bit_length() + 9) // 8, 'little', signed=True)
  206. return value