A native VirtualBox file format
Images for testing can be downloaded from
or you can convert images of other formats.
This page hosts a formal specification of VirtualBox Disk Image using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.
import (
"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
"io"
"bytes"
)
/**
* A native VirtualBox file format
*
* Images for testing can be downloaded from
*
* * <https://www.osboxes.org/virtualbox-images/>
* * <https://virtualboxes.org/images/>
*
* or you can convert images of other formats.
* @see <a href="https://github.com/qemu/qemu/blob/master/block/vdi.c">Source</a>
*/
type Vdi_ImageType int
const (
Vdi_ImageType__Dynamic Vdi_ImageType = 1
Vdi_ImageType__Static Vdi_ImageType = 2
Vdi_ImageType__Undo Vdi_ImageType = 3
Vdi_ImageType__Diff Vdi_ImageType = 4
)
var values_Vdi_ImageType = map[Vdi_ImageType]struct{}{1: {}, 2: {}, 3: {}, 4: {}}
func (v Vdi_ImageType) isDefined() bool {
_, ok := values_Vdi_ImageType[v]
return ok
}
type Vdi struct {
Header *Vdi_Header
_io *kaitai.Stream
_root *Vdi
_parent kaitai.Struct
_raw_blocksMap []byte
_f_blockDiscarded bool
blockDiscarded int
_f_blockUnallocated bool
blockUnallocated int
_f_blocksMap bool
blocksMap *Vdi_BlocksMap
_f_disk bool
disk *Vdi_Disk
}
func NewVdi() *Vdi {
return &Vdi{
}
}
func (this Vdi) IO_() *kaitai.Stream {
return this._io
}
func (this *Vdi) Read(io *kaitai.Stream, parent kaitai.Struct, root *Vdi) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp1 := NewVdi_Header()
err = tmp1.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Header = tmp1
return err
}
func (this *Vdi) BlockDiscarded() (v int, err error) {
if (this._f_blockDiscarded) {
return this.blockDiscarded, nil
}
this._f_blockDiscarded = true
this.blockDiscarded = int(uint32(4294967294))
return this.blockDiscarded, nil
}
func (this *Vdi) BlockUnallocated() (v int, err error) {
if (this._f_blockUnallocated) {
return this.blockUnallocated, nil
}
this._f_blockUnallocated = true
this.blockUnallocated = int(uint32(4294967295))
return this.blockUnallocated, nil
}
/**
* block_index = offset_in_virtual_disk / block_size actual_data_offset = blocks_map[block_index]*block_size+metadata_size+offset_in_block
* The blocks_map will take up blocks_in_image_max * sizeof(uint32_t) bytes; since the blocks_map is read and written in a single operation, its size needs to be limited to INT_MAX; furthermore, when opening an image, the blocks_map size is rounded up to be aligned on BDRV_SECTOR_SIZE. Therefore this should satisfy the following: blocks_in_image_max * sizeof(uint32_t) + BDRV_SECTOR_SIZE == INT_MAX + 1 (INT_MAX + 1 is the first value not representable as an int) This guarantees that any value below or equal to the constant will, when multiplied by sizeof(uint32_t) and rounded up to a BDRV_SECTOR_SIZE boundary, still be below or equal to INT_MAX.
*/
func (this *Vdi) BlocksMap() (v *Vdi_BlocksMap, err error) {
if (this._f_blocksMap) {
return this.blocksMap, nil
}
this._f_blocksMap = true
_pos, err := this._io.Pos()
if err != nil {
return nil, err
}
tmp2, err := this.Header.BlocksMapOffset()
if err != nil {
return nil, err
}
_, err = this._io.Seek(int64(tmp2), io.SeekStart)
if err != nil {
return nil, err
}
tmp3, err := this.Header.BlocksMapSize()
if err != nil {
return nil, err
}
tmp4, err := this._io.ReadBytes(int(tmp3))
if err != nil {
return nil, err
}
tmp4 = tmp4
this._raw_blocksMap = tmp4
_io__raw_blocksMap := kaitai.NewStream(bytes.NewReader(this._raw_blocksMap))
tmp5 := NewVdi_BlocksMap()
err = tmp5.Read(_io__raw_blocksMap, this, this._root)
if err != nil {
return nil, err
}
this.blocksMap = tmp5
_, err = this._io.Seek(_pos, io.SeekStart)
if err != nil {
return nil, err
}
return this.blocksMap, nil
}
func (this *Vdi) Disk() (v *Vdi_Disk, err error) {
if (this._f_disk) {
return this.disk, nil
}
this._f_disk = true
_pos, err := this._io.Pos()
if err != nil {
return nil, err
}
tmp6, err := this.Header.BlocksOffset()
if err != nil {
return nil, err
}
_, err = this._io.Seek(int64(tmp6), io.SeekStart)
if err != nil {
return nil, err
}
tmp7 := NewVdi_Disk()
err = tmp7.Read(this._io, this, this._root)
if err != nil {
return nil, err
}
this.disk = tmp7
_, err = this._io.Seek(_pos, io.SeekStart)
if err != nil {
return nil, err
}
return this.disk, nil
}
type Vdi_BlocksMap struct {
Index []*Vdi_BlocksMap_BlockIndex
_io *kaitai.Stream
_root *Vdi
_parent *Vdi
}
func NewVdi_BlocksMap() *Vdi_BlocksMap {
return &Vdi_BlocksMap{
}
}
func (this Vdi_BlocksMap) IO_() *kaitai.Stream {
return this._io
}
func (this *Vdi_BlocksMap) Read(io *kaitai.Stream, parent *Vdi, root *Vdi) (err error) {
this._io = io
this._parent = parent
this._root = root
for i := 0; i < int(this._root.Header.HeaderMain.BlocksInImage); i++ {
_ = i
tmp8 := NewVdi_BlocksMap_BlockIndex()
err = tmp8.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Index = append(this.Index, tmp8)
}
return err
}
type Vdi_BlocksMap_BlockIndex struct {
Index uint32
_io *kaitai.Stream
_root *Vdi
_parent *Vdi_BlocksMap
_f_block bool
block *Vdi_Disk_Block
_f_isAllocated bool
isAllocated bool
}
func NewVdi_BlocksMap_BlockIndex() *Vdi_BlocksMap_BlockIndex {
return &Vdi_BlocksMap_BlockIndex{
}
}
func (this Vdi_BlocksMap_BlockIndex) IO_() *kaitai.Stream {
return this._io
}
func (this *Vdi_BlocksMap_BlockIndex) Read(io *kaitai.Stream, parent *Vdi_BlocksMap, root *Vdi) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp9, err := this._io.ReadU4le()
if err != nil {
return err
}
this.Index = uint32(tmp9)
return err
}
func (this *Vdi_BlocksMap_BlockIndex) Block() (v *Vdi_Disk_Block, err error) {
if (this._f_block) {
return this.block, nil
}
this._f_block = true
tmp10, err := this.IsAllocated()
if err != nil {
return nil, err
}
if (tmp10) {
tmp11, err := this._root.Disk()
if err != nil {
return nil, err
}
this.block = tmp11.Blocks[this.Index]
}
return this.block, nil
}
func (this *Vdi_BlocksMap_BlockIndex) IsAllocated() (v bool, err error) {
if (this._f_isAllocated) {
return this.isAllocated, nil
}
this._f_isAllocated = true
tmp12, err := this._root.BlockDiscarded()
if err != nil {
return false, err
}
this.isAllocated = bool(this.Index < tmp12)
return this.isAllocated, nil
}
type Vdi_Disk struct {
Blocks []*Vdi_Disk_Block
_io *kaitai.Stream
_root *Vdi
_parent *Vdi
}
func NewVdi_Disk() *Vdi_Disk {
return &Vdi_Disk{
}
}
func (this Vdi_Disk) IO_() *kaitai.Stream {
return this._io
}
func (this *Vdi_Disk) Read(io *kaitai.Stream, parent *Vdi, root *Vdi) (err error) {
this._io = io
this._parent = parent
this._root = root
for i := 0; i < int(this._root.Header.HeaderMain.BlocksInImage); i++ {
_ = i
tmp13 := NewVdi_Disk_Block()
err = tmp13.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Blocks = append(this.Blocks, tmp13)
}
return err
}
type Vdi_Disk_Block struct {
Metadata []byte
Data []*Vdi_Disk_Block_Sector
_io *kaitai.Stream
_root *Vdi
_parent *Vdi_Disk
_raw_Data [][]byte
}
func NewVdi_Disk_Block() *Vdi_Disk_Block {
return &Vdi_Disk_Block{
}
}
func (this Vdi_Disk_Block) IO_() *kaitai.Stream {
return this._io
}
func (this *Vdi_Disk_Block) Read(io *kaitai.Stream, parent *Vdi_Disk, root *Vdi) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp14, err := this._io.ReadBytes(int(this._root.Header.HeaderMain.BlockMetadataSize))
if err != nil {
return err
}
tmp14 = tmp14
this.Metadata = tmp14
for i := 0;; i++ {
tmp15, err := this._io.EOF()
if err != nil {
return err
}
if tmp15 {
break
}
tmp16, err := this._io.ReadBytes(int(this._root.Header.HeaderMain.BlockDataSize))
if err != nil {
return err
}
tmp16 = tmp16
this._raw_Data = append(this._raw_Data, tmp16)
_io__raw_Data := kaitai.NewStream(bytes.NewReader(this._raw_Data[len(this._raw_Data) - 1]))
tmp17 := NewVdi_Disk_Block_Sector()
err = tmp17.Read(_io__raw_Data, this, this._root)
if err != nil {
return err
}
this.Data = append(this.Data, tmp17)
}
return err
}
type Vdi_Disk_Block_Sector struct {
Data []byte
_io *kaitai.Stream
_root *Vdi
_parent *Vdi_Disk_Block
}
func NewVdi_Disk_Block_Sector() *Vdi_Disk_Block_Sector {
return &Vdi_Disk_Block_Sector{
}
}
func (this Vdi_Disk_Block_Sector) IO_() *kaitai.Stream {
return this._io
}
func (this *Vdi_Disk_Block_Sector) Read(io *kaitai.Stream, parent *Vdi_Disk_Block, root *Vdi) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp18, err := this._io.ReadBytes(int(this._root.Header.HeaderMain.Geometry.SectorSize))
if err != nil {
return err
}
tmp18 = tmp18
this.Data = tmp18
return err
}
type Vdi_Header struct {
Text string
Signature []byte
Version *Vdi_Header_Version
HeaderSizeOptional uint32
HeaderMain *Vdi_Header_HeaderMain
_io *kaitai.Stream
_root *Vdi
_parent *Vdi
_raw_HeaderMain []byte
_f_blockSize bool
blockSize int
_f_blocksMapOffset bool
blocksMapOffset uint32
_f_blocksMapSize bool
blocksMapSize int
_f_blocksOffset bool
blocksOffset uint32
_f_headerSize bool
headerSize int
_f_subheaderSizeIsDynamic bool
subheaderSizeIsDynamic bool
}
func NewVdi_Header() *Vdi_Header {
return &Vdi_Header{
}
}
func (this Vdi_Header) IO_() *kaitai.Stream {
return this._io
}
func (this *Vdi_Header) Read(io *kaitai.Stream, parent *Vdi, root *Vdi) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp19, err := this._io.ReadBytes(int(64))
if err != nil {
return err
}
tmp19 = tmp19
this.Text = string(tmp19)
tmp20, err := this._io.ReadBytes(int(4))
if err != nil {
return err
}
tmp20 = tmp20
this.Signature = tmp20
if !(bytes.Equal(this.Signature, []uint8{127, 16, 218, 190})) {
return kaitai.NewValidationNotEqualError([]uint8{127, 16, 218, 190}, this.Signature, this._io, "/types/header/seq/1")
}
tmp21 := NewVdi_Header_Version()
err = tmp21.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Version = tmp21
tmp22, err := this.SubheaderSizeIsDynamic()
if err != nil {
return err
}
if (tmp22) {
tmp23, err := this._io.ReadU4le()
if err != nil {
return err
}
this.HeaderSizeOptional = uint32(tmp23)
}
tmp24, err := this.HeaderSize()
if err != nil {
return err
}
tmp25, err := this._io.ReadBytes(int(tmp24))
if err != nil {
return err
}
tmp25 = tmp25
this._raw_HeaderMain = tmp25
_io__raw_HeaderMain := kaitai.NewStream(bytes.NewReader(this._raw_HeaderMain))
tmp26 := NewVdi_Header_HeaderMain()
err = tmp26.Read(_io__raw_HeaderMain, this, this._root)
if err != nil {
return err
}
this.HeaderMain = tmp26
return err
}
func (this *Vdi_Header) BlockSize() (v int, err error) {
if (this._f_blockSize) {
return this.blockSize, nil
}
this._f_blockSize = true
this.blockSize = int(this.HeaderMain.BlockMetadataSize + this.HeaderMain.BlockDataSize)
return this.blockSize, nil
}
func (this *Vdi_Header) BlocksMapOffset() (v uint32, err error) {
if (this._f_blocksMapOffset) {
return this.blocksMapOffset, nil
}
this._f_blocksMapOffset = true
this.blocksMapOffset = uint32(this.HeaderMain.BlocksMapOffset)
return this.blocksMapOffset, nil
}
func (this *Vdi_Header) BlocksMapSize() (v int, err error) {
if (this._f_blocksMapSize) {
return this.blocksMapSize, nil
}
this._f_blocksMapSize = true
this.blocksMapSize = int((((this.HeaderMain.BlocksInImage * 4 + this.HeaderMain.Geometry.SectorSize) - 1) / this.HeaderMain.Geometry.SectorSize) * this.HeaderMain.Geometry.SectorSize)
return this.blocksMapSize, nil
}
func (this *Vdi_Header) BlocksOffset() (v uint32, err error) {
if (this._f_blocksOffset) {
return this.blocksOffset, nil
}
this._f_blocksOffset = true
this.blocksOffset = uint32(this.HeaderMain.OffsetData)
return this.blocksOffset, nil
}
func (this *Vdi_Header) HeaderSize() (v int, err error) {
if (this._f_headerSize) {
return this.headerSize, nil
}
this._f_headerSize = true
var tmp27 uint32;
tmp28, err := this.SubheaderSizeIsDynamic()
if err != nil {
return 0, err
}
if (tmp28) {
tmp27 = this.HeaderSizeOptional
} else {
tmp27 = 336
}
this.headerSize = int(tmp27)
return this.headerSize, nil
}
func (this *Vdi_Header) SubheaderSizeIsDynamic() (v bool, err error) {
if (this._f_subheaderSizeIsDynamic) {
return this.subheaderSizeIsDynamic, nil
}
this._f_subheaderSizeIsDynamic = true
this.subheaderSizeIsDynamic = bool(this.Version.Major >= 1)
return this.subheaderSizeIsDynamic, nil
}
type Vdi_Header_HeaderMain struct {
ImageType Vdi_ImageType
ImageFlags *Vdi_Header_HeaderMain_Flags
Description string
BlocksMapOffset uint32
OffsetData uint32
Geometry *Vdi_Header_HeaderMain_Geometry
Reserved1 uint32
DiskSize uint64
BlockDataSize uint32
BlockMetadataSize uint32
BlocksInImage uint32
BlocksAllocated uint32
UuidImage *Vdi_Header_Uuid
UuidLastSnap *Vdi_Header_Uuid
UuidLink *Vdi_Header_Uuid
UuidParent *Vdi_Header_Uuid
LchcGeometry *Vdi_Header_HeaderMain_Geometry
_io *kaitai.Stream
_root *Vdi
_parent *Vdi_Header
}
func NewVdi_Header_HeaderMain() *Vdi_Header_HeaderMain {
return &Vdi_Header_HeaderMain{
}
}
func (this Vdi_Header_HeaderMain) IO_() *kaitai.Stream {
return this._io
}
func (this *Vdi_Header_HeaderMain) Read(io *kaitai.Stream, parent *Vdi_Header, root *Vdi) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp29, err := this._io.ReadU4le()
if err != nil {
return err
}
this.ImageType = Vdi_ImageType(tmp29)
tmp30 := NewVdi_Header_HeaderMain_Flags()
err = tmp30.Read(this._io, this, this._root)
if err != nil {
return err
}
this.ImageFlags = tmp30
tmp31, err := this._io.ReadBytes(int(256))
if err != nil {
return err
}
tmp31 = tmp31
this.Description = string(tmp31)
if (this._parent.Version.Major >= 1) {
tmp32, err := this._io.ReadU4le()
if err != nil {
return err
}
this.BlocksMapOffset = uint32(tmp32)
}
if (this._parent.Version.Major >= 1) {
tmp33, err := this._io.ReadU4le()
if err != nil {
return err
}
this.OffsetData = uint32(tmp33)
}
tmp34 := NewVdi_Header_HeaderMain_Geometry()
err = tmp34.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Geometry = tmp34
if (this._parent.Version.Major >= 1) {
tmp35, err := this._io.ReadU4le()
if err != nil {
return err
}
this.Reserved1 = uint32(tmp35)
}
tmp36, err := this._io.ReadU8le()
if err != nil {
return err
}
this.DiskSize = uint64(tmp36)
tmp37, err := this._io.ReadU4le()
if err != nil {
return err
}
this.BlockDataSize = uint32(tmp37)
if (this._parent.Version.Major >= 1) {
tmp38, err := this._io.ReadU4le()
if err != nil {
return err
}
this.BlockMetadataSize = uint32(tmp38)
}
tmp39, err := this._io.ReadU4le()
if err != nil {
return err
}
this.BlocksInImage = uint32(tmp39)
tmp40, err := this._io.ReadU4le()
if err != nil {
return err
}
this.BlocksAllocated = uint32(tmp40)
tmp41 := NewVdi_Header_Uuid()
err = tmp41.Read(this._io, this, this._root)
if err != nil {
return err
}
this.UuidImage = tmp41
tmp42 := NewVdi_Header_Uuid()
err = tmp42.Read(this._io, this, this._root)
if err != nil {
return err
}
this.UuidLastSnap = tmp42
tmp43 := NewVdi_Header_Uuid()
err = tmp43.Read(this._io, this, this._root)
if err != nil {
return err
}
this.UuidLink = tmp43
if (this._parent.Version.Major >= 1) {
tmp44 := NewVdi_Header_Uuid()
err = tmp44.Read(this._io, this, this._root)
if err != nil {
return err
}
this.UuidParent = tmp44
}
tmp45, err := this._io.Pos()
if err != nil {
return err
}
tmp46, err := this._io.Size()
if err != nil {
return err
}
if ( ((this._parent.Version.Major >= 1) && (tmp45 + 16 <= tmp46)) ) {
tmp47 := NewVdi_Header_HeaderMain_Geometry()
err = tmp47.Read(this._io, this, this._root)
if err != nil {
return err
}
this.LchcGeometry = tmp47
}
return err
}
/**
* Size of block (bytes).
*/
type Vdi_Header_HeaderMain_Flags struct {
Reserved0 uint64
ZeroExpand bool
Reserved1 uint64
Diff bool
Fixed bool
Reserved2 uint64
_io *kaitai.Stream
_root *Vdi
_parent *Vdi_Header_HeaderMain
}
func NewVdi_Header_HeaderMain_Flags() *Vdi_Header_HeaderMain_Flags {
return &Vdi_Header_HeaderMain_Flags{
}
}
func (this Vdi_Header_HeaderMain_Flags) IO_() *kaitai.Stream {
return this._io
}
func (this *Vdi_Header_HeaderMain_Flags) Read(io *kaitai.Stream, parent *Vdi_Header_HeaderMain, root *Vdi) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp48, err := this._io.ReadBitsIntBe(15)
if err != nil {
return err
}
this.Reserved0 = tmp48
tmp49, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.ZeroExpand = tmp49 != 0
tmp50, err := this._io.ReadBitsIntBe(6)
if err != nil {
return err
}
this.Reserved1 = tmp50
tmp51, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.Diff = tmp51 != 0
tmp52, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.Fixed = tmp52 != 0
tmp53, err := this._io.ReadBitsIntBe(8)
if err != nil {
return err
}
this.Reserved2 = tmp53
return err
}
type Vdi_Header_HeaderMain_Geometry struct {
Cylinders uint32
Heads uint32
Sectors uint32
SectorSize uint32
_io *kaitai.Stream
_root *Vdi
_parent *Vdi_Header_HeaderMain
}
func NewVdi_Header_HeaderMain_Geometry() *Vdi_Header_HeaderMain_Geometry {
return &Vdi_Header_HeaderMain_Geometry{
}
}
func (this Vdi_Header_HeaderMain_Geometry) IO_() *kaitai.Stream {
return this._io
}
func (this *Vdi_Header_HeaderMain_Geometry) Read(io *kaitai.Stream, parent *Vdi_Header_HeaderMain, root *Vdi) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp54, err := this._io.ReadU4le()
if err != nil {
return err
}
this.Cylinders = uint32(tmp54)
tmp55, err := this._io.ReadU4le()
if err != nil {
return err
}
this.Heads = uint32(tmp55)
tmp56, err := this._io.ReadU4le()
if err != nil {
return err
}
this.Sectors = uint32(tmp56)
tmp57, err := this._io.ReadU4le()
if err != nil {
return err
}
this.SectorSize = uint32(tmp57)
return err
}
type Vdi_Header_Uuid struct {
Uuid []byte
_io *kaitai.Stream
_root *Vdi
_parent *Vdi_Header_HeaderMain
}
func NewVdi_Header_Uuid() *Vdi_Header_Uuid {
return &Vdi_Header_Uuid{
}
}
func (this Vdi_Header_Uuid) IO_() *kaitai.Stream {
return this._io
}
func (this *Vdi_Header_Uuid) Read(io *kaitai.Stream, parent *Vdi_Header_HeaderMain, root *Vdi) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp58, err := this._io.ReadBytes(int(16))
if err != nil {
return err
}
tmp58 = tmp58
this.Uuid = tmp58
return err
}
type Vdi_Header_Version struct {
Major uint16
Minor uint16
_io *kaitai.Stream
_root *Vdi
_parent *Vdi_Header
}
func NewVdi_Header_Version() *Vdi_Header_Version {
return &Vdi_Header_Version{
}
}
func (this Vdi_Header_Version) IO_() *kaitai.Stream {
return this._io
}
func (this *Vdi_Header_Version) Read(io *kaitai.Stream, parent *Vdi_Header, root *Vdi) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp59, err := this._io.ReadU2le()
if err != nil {
return err
}
this.Major = uint16(tmp59)
tmp60, err := this._io.ReadU2le()
if err != nil {
return err
}
this.Minor = uint16(tmp60)
return err
}