VHD
VHD 结构是一种虚拟磁盘的实现方式,即通过文件来模拟物理磁盘的方式来存储数据。
VHD结构有2种实现方式:固定方式和动态方式。本文只讨论固定方式。
固定方式就是用真实大小的文件模拟同样大小的一个虚拟磁盘。
VHD 头部
VHD和其他文件不同,VHD其信息放在尾部,也就是最后一个扇区中。我们应当首先读取最后一个扇区的内容。
字段 | 字节数 | 说明 |
---|---|---|
cookie | 8 | 标识,应为conectix,用于判断VHD是否有效 |
features | 4 | 0: HD_NO_FEATURES 1: HD_TEMPORARY 2: HD_RESERVED |
ff_version | 4 | VHD版本 |
data_offset | 8 | 固定磁盘,总是0xFFFFFFFF。 |
timestamp | 4 | VHD的创建时间,指2000年1月1日00:00:00至今的秒数 |
crtr_app | 4 | 创建者 |
crtr_ver | 4 | 创建版本 |
crtr_os | 4 | 创建者系统 |
orig_size | 8 | 创建的虚拟磁盘大小,这个大小指虚拟出来的磁盘的可用寻址空间。 |
curr_size | 8 | 用于vhd在线扩容后的最后大小表述,如果没有扩容,和orig_size相同。 |
geometry | 4 | VHD的C/H/S结构参数 柱面数2字节,磁头数1字节,扇区数1字节 |
type | 4 | 0: HD_TYPE_NONE 2: HD_TYPE_FIXED 固定磁盘 3: HD_TYPE_DYNAMIC 动态磁盘 4: HD_TYPE_DIFF 差异磁盘 |
checksum | 4 | 本扇区的校验和 |
uuid | 16 | 磁盘标识,在差异磁盘中,决定了VHD间的主从关系。 |
saved | 1 | 是否在保存状态 |
hidden | 1 | VDI是否隐藏 |
VHD 写入
VHD文件规定读写需要针对磁道,读写必须要在某个磁道的开头处,然后读取n个磁道(n*512字节)。如果你的数据不够512的倍数,那么写入的时候需要将数据凑到512字节的倍数,不够的部分补0。
import datetime
import logging
class Vhd:
def __init__(self, file_name):
self.head = {}
self.file_name = file_name
self.f = open(self.file_name, 'rb+')
self.head_info()
def head_info(self):
self.f.seek(-512, 2)
self.head["cookie"] = self.f.read(8).decode()
self.head["features"] = self.f.read(4)
self.head["ff_version"] = self.f.read(4)
self.head["data_offset"] = self.f.read(8)
t = int.from_bytes(self.f.read(4))
t = datetime.datetime(2000, 1, 1, 8) + datetime.timedelta(seconds=t)
self.head["timestamp"] = t.strftime("%Y-%m-%d %H:%M:%S")
self.head["crtr_app"] = self.f.read(4).decode()
self.head["crtr_ver"] = self.f.read(4)
self.head["crtr_os"] = self.f.read(4)
self.head["orig_size"] = int.from_bytes(self.f.read(8))
self.head["curr_size"] = int.from_bytes(self.f.read(8))
cylinder = int.from_bytes(self.f.read(2))
head = int.from_bytes(self.f.read(1))
sector = int.from_bytes(self.f.read(1))
self.head["geometry"] = (cylinder, head, sector)
type_ = int.from_bytes(self.f.read(4))
if type_ == 0:
type_ = "HD_TYPE_NONE"
elif type_ == 2:
type_ = "HD_TYPE_FIXED"
elif type_ == 3:
type_ = "HD_TYPE_DYNAMIC"
elif type_ == 4:
type_ = "HD_TYPE_DIFF"
self.head["type"] = type_
self.head["checksum"] = self.f.read(4)
self.head["uuid"] = self.f.read(16)
self.head["saved"] = bool(int.from_bytes(self.f.read(1)))
self.head["hidden"] = bool(int.from_bytes(self.f.read(1)))
def __str__(self):
return str(self.head)
def write(self, bin_file, *, lba=None, cylinder=None, head=None, sector=None):
if lba is None:
lba = cylinder * self.head['geometry'][1] * self.head['geometry'][2] + head * self.head['geometry'][
2] + sector - 1
start_pos = lba * 512
with open(bin_file, 'rb') as f:
self.f.seek(-512, 2)
safe = self.f.tell()
f.seek(0, 2)
write_file_length = f.tell()
if start_pos + write_file_length > safe:
raise Exception('No Enough Space')
self.f.seek(start_pos, 0)
f.seek(0, 0)
while True:
data = f.read(512)
if not data:
break
self.f.write(data)
logging.info(f'file write successfully')
def __del__(self):
self.f.close()
Comments | NOTHING