summaryrefslogtreecommitdiff
path: root/scripts/vhd.py
blob: 6a268924145ed6eeff4508313cdb5033b6ff451f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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())