diff options
-rwxr-xr-x | scripts/vhd.py | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/scripts/vhd.py b/scripts/vhd.py new file mode 100755 index 0000000..6a26892 --- /dev/null +++ b/scripts/vhd.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright: 2015 Bastian Blank +# License: MIT, see LICENSE.txt for details. + +import array +import struct +import time +import sys +import os + +from uuid import uuid4 + + +class VHDFooter: + _struct = struct.Struct('>8sLLQL4sL4sQQ4sLL16sB427x') + size = _struct.size + + vhd_timestamp_base = 946684800 + + def __init__(self, size, uuid=None, timestamp=None): + self.size = size + self.timestamp = timestamp or (int(time.time()) - self.vhd_timestamp_base) + self.uuid = uuid or uuid4() + + @staticmethod + def _checksum(msg): + return 0x100000000 + ~sum(array.array("B", msg)) + + def _pack_geometry(self): + sectors = self.size // 512 + + if sectors > 65535 * 16 * 255: + sectors = 65535 * 16 * 255 + + if sectors >= 65535 * 16 * 63: + sectorsPerTrack = 255 + heads = 16 + cylinderTimesHeads = sectors // sectorsPerTrack + + else: + sectorsPerTrack = 17 + cylinderTimesHeads = sectors // sectorsPerTrack + + heads = (cylinderTimesHeads + 1023) // 1024 + + if heads < 4: + heads = 4 + if cylinderTimesHeads >= (heads * 1024) or heads > 16: + sectorsPerTrack = 31 + heads = 16 + cylinderTimesHeads = sectors // sectorsPerTrack + if cylinderTimesHeads >= (heads * 1024): + sectorsPerTrack = 63 + heads = 16 + cylinderTimesHeads = sectors // sectorsPerTrack + + cylinders = cylinderTimesHeads // heads + + return struct.pack('>HBB', cylinders, heads, sectorsPerTrack) + + def _pack(self, checksum): + return self._struct.pack( + b'conectix', # Cookie + 0x00000002, # Features + 0x00010000, # File Format Version + 0xffffffffffffffff, # Data Offset + self.timestamp, # Time Stamp + b'qemu', # Creator Application + 0x00010000, # Creator Version + b'Wi2k', # Creator Host OS + self.size, # Original Size + self.size, # Current Size + self._pack_geometry(), # Disk Geometry + 2, # Disk Type + checksum, # Checksum + self.uuid.bytes, # Unique Id + 0, # Saved State + ) + + def pack(self): + c = self._checksum(self._pack(0)) + return self._pack(c) + +with open(sys.argv[1], 'rb+') as f: + f.seek(0, 2) + image_size = f.tell() + image_size_complete = image_size + VHDFooter.size + footer = VHDFooter(image_size) + f.write(footer.pack()) |