qemu_reader.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. # Copyright 2017 Cloudbase Solutions Srl
  2. # All Rights Reserved.
  3. import contextlib
  4. import ctypes
  5. from coriolis import exception
  6. from coriolis import qemu
  7. class QEMUDiskImageReaderImpl(object):
  8. def __init__(self, path):
  9. self._blk = None
  10. self._bs = None
  11. self._total_sectors = None
  12. self._block_driver_state = None
  13. self._buf = None
  14. self._buf_size = None
  15. self._path = path
  16. def close(self):
  17. if self._buf is not None:
  18. qemu.qemu_vfree(self._buf)
  19. self._buf = None
  20. self._buf_size = None
  21. if self._blk is not None:
  22. qemu.blk_unref(self._blk)
  23. self._blk = None
  24. self._bs = None
  25. self._total_sectors = None
  26. self._block_driver_state = None
  27. def _open(self):
  28. error = ctypes.POINTER(qemu.Error)()
  29. options = qemu.qdict_new()
  30. blk = qemu.blk_new_open(
  31. self._path.encode(), None, options, 0, ctypes.byref(error))
  32. if not blk:
  33. raise exception.QEMUException(error.contents.msg.decode())
  34. self._blk = blk
  35. self._bs = qemu.blk_bs(blk)
  36. self._total_sectors = qemu.blk_nb_sectors(blk)
  37. self._block_driver_state = ctypes.c_void_p()
  38. @property
  39. def disk_size(self):
  40. return self._total_sectors << qemu.BDRV_SECTOR_BITS
  41. def _get_sectors(self, offset, size):
  42. start_sector = offset >> qemu.BDRV_SECTOR_BITS
  43. return (start_sector,
  44. min(self._total_sectors - start_sector,
  45. size >> qemu.BDRV_SECTOR_BITS))
  46. def get_block_status(self, offset, size):
  47. start_sector, num_sectors = self._get_sectors(offset, size)
  48. sectors = 0
  49. block_status = None
  50. while True:
  51. pnum = ctypes.c_int(0)
  52. status = qemu.bdrv_get_block_status_above(
  53. self._bs, None, start_sector + sectors, num_sectors - sectors,
  54. ctypes.byref(pnum), ctypes.byref(self._block_driver_state))
  55. if status < 0 or pnum.value == 0:
  56. raise exception.QEMUException(
  57. 'bdrv_get_block_status_above failed')
  58. allocated = (status & qemu.BDRV_BLOCK_ALLOCATED) > 0
  59. zero_block = (status & qemu.BDRV_BLOCK_ZERO) > 0
  60. if block_status and block_status != (allocated, zero_block):
  61. break
  62. block_status = (allocated, zero_block)
  63. sectors += pnum.value
  64. if sectors >= num_sectors:
  65. break
  66. block_size = min(num_sectors, sectors) << qemu.BDRV_SECTOR_BITS
  67. return block_status + (block_size,)
  68. def read(self, offset, size):
  69. _, num_sectors = self._get_sectors(offset, size)
  70. if not self._buf_size or self._buf_size < size:
  71. if self._buf is not None:
  72. qemu.qemu_vfree(self._buf)
  73. self._buf = qemu.blk_blockalign(self._blk, size)
  74. self._buf_size = size
  75. read_size = num_sectors << qemu.BDRV_SECTOR_BITS
  76. ret = qemu.blk_pread(
  77. self._blk, offset, self._buf, read_size)
  78. if ret < 0:
  79. raise exception.QEMUException("blk_pread failed")
  80. return (ctypes.c_ubyte * read_size).from_address(self._buf)
  81. class QEMUDiskImageReader(object):
  82. @contextlib.contextmanager
  83. def open(self, path):
  84. impl = None
  85. try:
  86. impl = QEMUDiskImageReaderImpl(path)
  87. impl._open()
  88. yield impl
  89. finally:
  90. if impl:
  91. impl.close()
  92. def _qemu_init():
  93. error = ctypes.POINTER(qemu.Error)()
  94. qemu.module_call_init(qemu.MODULE_INIT_TRACE)
  95. qemu.error_set_progname('coriolis'.encode())
  96. qemu.qemu_init_exec_dir('.'.encode())
  97. if qemu.qemu_init_main_loop(ctypes.byref(error)):
  98. raise exception.QEMUException(error.contents.msg.decode())
  99. if qemu.qcrypto_init(ctypes.byref(error)):
  100. raise exception.QEMUException(error.contents.msg.decode())
  101. qemu.module_call_init(qemu.MODULE_INIT_QOM)
  102. qemu.bdrv_init()
  103. _qemu_init()