VirtualBox Disk Image: Java (read-write) parsing library

A native VirtualBox file format

Images for testing can be downloaded from

or you can convert images of other formats.

Application

["VirtualBox", "QEMU", "VMWare Workstation"]

File extension

vdi

KS implementation details

License: GPL-3.0-or-later

References

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.

Java (read-write) source code to parse VirtualBox Disk Image

Vdi.java

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

import io.kaitai.struct.ByteBufferKaitaiStream;
import io.kaitai.struct.KaitaiStruct;
import io.kaitai.struct.KaitaiStream;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Objects;
import io.kaitai.struct.ConsistencyError;
import java.util.ArrayList;
import java.util.List;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.nio.charset.Charset;


/**
 * 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>
 */
public class Vdi extends KaitaiStruct.ReadWrite {
    public static Vdi fromFile(String fileName) throws IOException {
        return new Vdi(new ByteBufferKaitaiStream(fileName));
    }

    public enum ImageType {
        DYNAMIC(1),
        STATIC(2),
        UNDO(3),
        DIFF(4);

        private final long id;
        ImageType(long id) { this.id = id; }
        public long id() { return id; }
        private static final Map<Long, ImageType> byId = new HashMap<Long, ImageType>(4);
        static {
            for (ImageType e : ImageType.values())
                byId.put(e.id(), e);
        }
        public static ImageType byId(long id) { return byId.get(id); }
    }
    public Vdi() {
        this(null, null, null);
    }

    public Vdi(KaitaiStream _io) {
        this(_io, null, null);
    }

    public Vdi(KaitaiStream _io, KaitaiStruct.ReadWrite _parent) {
        this(_io, _parent, null);
    }

    public Vdi(KaitaiStream _io, KaitaiStruct.ReadWrite _parent, Vdi _root) {
        super(_io);
        this._parent = _parent;
        this._root = _root == null ? this : _root;
    }
    public void _read() {
        this.header = new Header(this._io, this, _root);
        this.header._read();
        _dirty = false;
    }

    public void _fetchInstances() {
        this.header._fetchInstances();
        blocksMap();
        if (this.blocksMap != null) {
            this.blocksMap._fetchInstances();
        }
        disk();
        if (this.disk != null) {
            this.disk._fetchInstances();
        }
    }

    public void _write_Seq() {
        _assertNotDirty();
        _shouldWriteBlocksMap = _enabledBlocksMap;
        _shouldWriteDisk = _enabledDisk;
        this.header._write_Seq(this._io);
    }

    public void _check() {
        if (!Objects.equals(this.header._root(), _root()))
            throw new ConsistencyError("header", _root(), this.header._root());
        if (!Objects.equals(this.header._parent(), this))
            throw new ConsistencyError("header", this, this.header._parent());
        if (_enabledBlocksMap) {
            if (!Objects.equals(this.blocksMap._root(), _root()))
                throw new ConsistencyError("blocks_map", _root(), this.blocksMap._root());
            if (!Objects.equals(this.blocksMap._parent(), this))
                throw new ConsistencyError("blocks_map", this, this.blocksMap._parent());
        }
        if (_enabledDisk) {
            if (!Objects.equals(this.disk._root(), _root()))
                throw new ConsistencyError("disk", _root(), this.disk._root());
            if (!Objects.equals(this.disk._parent(), this))
                throw new ConsistencyError("disk", this, this.disk._parent());
        }
        _dirty = false;
    }
    public static class BlocksMap extends KaitaiStruct.ReadWrite {
        public static BlocksMap fromFile(String fileName) throws IOException {
            return new BlocksMap(new ByteBufferKaitaiStream(fileName));
        }
        public BlocksMap() {
            this(null, null, null);
        }

        public BlocksMap(KaitaiStream _io) {
            this(_io, null, null);
        }

        public BlocksMap(KaitaiStream _io, Vdi _parent) {
            this(_io, _parent, null);
        }

        public BlocksMap(KaitaiStream _io, Vdi _parent, Vdi _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
        }
        public void _read() {
            this.index = new ArrayList<BlockIndex>();
            for (int i = 0; i < _root().header().headerMain().blocksInImage(); i++) {
                BlockIndex _t_index = new BlockIndex(this._io, this, _root);
                try {
                    _t_index._read();
                } finally {
                    this.index.add(_t_index);
                }
            }
            _dirty = false;
        }

        public void _fetchInstances() {
            for (int i = 0; i < this.index.size(); i++) {
                this.index.get(((Number) (i)).intValue())._fetchInstances();
            }
        }

        public void _write_Seq() {
            _assertNotDirty();
            for (int i = 0; i < this.index.size(); i++) {
                this.index.get(((Number) (i)).intValue())._write_Seq(this._io);
            }
        }

        public void _check() {
            if (this.index.size() != _root().header().headerMain().blocksInImage())
                throw new ConsistencyError("index", _root().header().headerMain().blocksInImage(), this.index.size());
            for (int i = 0; i < this.index.size(); i++) {
                if (!Objects.equals(this.index.get(((Number) (i)).intValue())._root(), _root()))
                    throw new ConsistencyError("index", _root(), this.index.get(((Number) (i)).intValue())._root());
                if (!Objects.equals(this.index.get(((Number) (i)).intValue())._parent(), this))
                    throw new ConsistencyError("index", this, this.index.get(((Number) (i)).intValue())._parent());
            }
            _dirty = false;
        }
        public static class BlockIndex extends KaitaiStruct.ReadWrite {
            public static BlockIndex fromFile(String fileName) throws IOException {
                return new BlockIndex(new ByteBufferKaitaiStream(fileName));
            }
            public BlockIndex() {
                this(null, null, null);
            }

            public BlockIndex(KaitaiStream _io) {
                this(_io, null, null);
            }

            public BlockIndex(KaitaiStream _io, Vdi.BlocksMap _parent) {
                this(_io, _parent, null);
            }

            public BlockIndex(KaitaiStream _io, Vdi.BlocksMap _parent, Vdi _root) {
                super(_io);
                this._parent = _parent;
                this._root = _root;
            }
            public void _read() {
                this.index = this._io.readU4le();
                _dirty = false;
            }

            public void _fetchInstances() {
            }

            public void _write_Seq() {
                _assertNotDirty();
                this._io.writeU4le(this.index);
            }

            public void _check() {
                _dirty = false;
            }
            private Block block;
            public Block block() {
                if (this.block != null)
                    return this.block;
                if (isAllocated()) {
                    this.block = _root().disk().blocks().get(((Number) (index())).intValue());
                }
                return this.block;
            }
            public void _invalidateBlock() { this.block = null; }
            private Boolean isAllocated;
            public Boolean isAllocated() {
                if (this.isAllocated != null)
                    return this.isAllocated;
                this.isAllocated = index() < _root().blockDiscarded();
                return this.isAllocated;
            }
            public void _invalidateIsAllocated() { this.isAllocated = null; }
            private long index;
            private Vdi _root;
            private Vdi.BlocksMap _parent;
            public long index() { return index; }
            public void setIndex(long _v) { _dirty = true; index = _v; }
            public Vdi _root() { return _root; }
            public void set_root(Vdi _v) { _dirty = true; _root = _v; }
            public Vdi.BlocksMap _parent() { return _parent; }
            public void set_parent(Vdi.BlocksMap _v) { _dirty = true; _parent = _v; }
        }
        private List<BlockIndex> index;
        private Vdi _root;
        private Vdi _parent;
        public List<BlockIndex> index() { return index; }
        public void setIndex(List<BlockIndex> _v) { _dirty = true; index = _v; }
        public Vdi _root() { return _root; }
        public void set_root(Vdi _v) { _dirty = true; _root = _v; }
        public Vdi _parent() { return _parent; }
        public void set_parent(Vdi _v) { _dirty = true; _parent = _v; }
    }
    public static class Disk extends KaitaiStruct.ReadWrite {
        public static Disk fromFile(String fileName) throws IOException {
            return new Disk(new ByteBufferKaitaiStream(fileName));
        }
        public Disk() {
            this(null, null, null);
        }

        public Disk(KaitaiStream _io) {
            this(_io, null, null);
        }

        public Disk(KaitaiStream _io, Vdi _parent) {
            this(_io, _parent, null);
        }

        public Disk(KaitaiStream _io, Vdi _parent, Vdi _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
        }
        public void _read() {
            this.blocks = new ArrayList<Block>();
            for (int i = 0; i < _root().header().headerMain().blocksInImage(); i++) {
                Block _t_blocks = new Block(this._io, this, _root);
                try {
                    _t_blocks._read();
                } finally {
                    this.blocks.add(_t_blocks);
                }
            }
            _dirty = false;
        }

        public void _fetchInstances() {
            for (int i = 0; i < this.blocks.size(); i++) {
                this.blocks.get(((Number) (i)).intValue())._fetchInstances();
            }
        }

        public void _write_Seq() {
            _assertNotDirty();
            for (int i = 0; i < this.blocks.size(); i++) {
                this.blocks.get(((Number) (i)).intValue())._write_Seq(this._io);
            }
        }

        public void _check() {
            if (this.blocks.size() != _root().header().headerMain().blocksInImage())
                throw new ConsistencyError("blocks", _root().header().headerMain().blocksInImage(), this.blocks.size());
            for (int i = 0; i < this.blocks.size(); i++) {
                if (!Objects.equals(this.blocks.get(((Number) (i)).intValue())._root(), _root()))
                    throw new ConsistencyError("blocks", _root(), this.blocks.get(((Number) (i)).intValue())._root());
                if (!Objects.equals(this.blocks.get(((Number) (i)).intValue())._parent(), this))
                    throw new ConsistencyError("blocks", this, this.blocks.get(((Number) (i)).intValue())._parent());
            }
            _dirty = false;
        }
        public static class Block extends KaitaiStruct.ReadWrite {
            public static Block fromFile(String fileName) throws IOException {
                return new Block(new ByteBufferKaitaiStream(fileName));
            }
            public Block() {
                this(null, null, null);
            }

            public Block(KaitaiStream _io) {
                this(_io, null, null);
            }

            public Block(KaitaiStream _io, Vdi.Disk _parent) {
                this(_io, _parent, null);
            }

            public Block(KaitaiStream _io, Vdi.Disk _parent, Vdi _root) {
                super(_io);
                this._parent = _parent;
                this._root = _root;
            }
            public void _read() {
                this.metadata = this._io.readBytes(_root().header().headerMain().blockMetadataSize());
                this._raw_data = new ArrayList<byte[]>();
                this.data = new ArrayList<Sector>();
                {
                    int i = 0;
                    while (!this._io.isEof()) {
                        this._raw_data.add(this._io.readBytes(_root().header().headerMain().blockDataSize()));
                        KaitaiStream _io__raw_data = new ByteBufferKaitaiStream(this._raw_data.get(this._raw_data.size() - 1));
                        Sector _t_data = new Sector(_io__raw_data, this, _root);
                        try {
                            _t_data._read();
                        } finally {
                            this.data.add(_t_data);
                        }
                        i++;
                    }
                }
                _dirty = false;
            }

            public void _fetchInstances() {
                for (int i = 0; i < this.data.size(); i++) {
                    this.data.get(((Number) (i)).intValue())._fetchInstances();
                }
            }

            public void _write_Seq() {
                _assertNotDirty();
                this._io.writeBytes(this.metadata);
                this._raw_data = new ArrayList<byte[]>();
                for (int i = 0; i < this.data.size(); i++) {
                    if (this._io.isEof())
                        throw new ConsistencyError("data", 0, this._io.size() - this._io.pos());
                    final KaitaiStream _io__raw_data = new ByteBufferKaitaiStream(_root().header().headerMain().blockDataSize());
                    this._io.addChildStream(_io__raw_data);
                    {
                        long _pos2 = this._io.pos();
                        this._io.seek(this._io.pos() + (_root().header().headerMain().blockDataSize()));
                        final Block _this = this;
                        final int _i = i;
                        _io__raw_data.setWriteBackHandler(new KaitaiStream.WriteBackHandler(_pos2) {
                            @Override
                            protected void write(KaitaiStream parent) {
                                _this._raw_data.add(_io__raw_data.toByteArray());
                                if (_this._raw_data.get(((Number) (_i)).intValue()).length != _root().header().headerMain().blockDataSize())
                                    throw new ConsistencyError("raw(data)", _root().header().headerMain().blockDataSize(), _this._raw_data.get(((Number) (_i)).intValue()).length);
                                parent.writeBytes(_this._raw_data.get(((Number) (_i)).intValue()));
                            }
                        });
                    }
                    this.data.get(((Number) (i)).intValue())._write_Seq(_io__raw_data);
                }
                if (!(this._io.isEof()))
                    throw new ConsistencyError("data", 0, this._io.size() - this._io.pos());
            }

            public void _check() {
                if (this.metadata.length != _root().header().headerMain().blockMetadataSize())
                    throw new ConsistencyError("metadata", _root().header().headerMain().blockMetadataSize(), this.metadata.length);
                for (int i = 0; i < this.data.size(); i++) {
                    if (!Objects.equals(this.data.get(((Number) (i)).intValue())._root(), _root()))
                        throw new ConsistencyError("data", _root(), this.data.get(((Number) (i)).intValue())._root());
                    if (!Objects.equals(this.data.get(((Number) (i)).intValue())._parent(), this))
                        throw new ConsistencyError("data", this, this.data.get(((Number) (i)).intValue())._parent());
                }
                _dirty = false;
            }
            public static class Sector extends KaitaiStruct.ReadWrite {
                public static Sector fromFile(String fileName) throws IOException {
                    return new Sector(new ByteBufferKaitaiStream(fileName));
                }
                public Sector() {
                    this(null, null, null);
                }

                public Sector(KaitaiStream _io) {
                    this(_io, null, null);
                }

                public Sector(KaitaiStream _io, Vdi.Disk.Block _parent) {
                    this(_io, _parent, null);
                }

                public Sector(KaitaiStream _io, Vdi.Disk.Block _parent, Vdi _root) {
                    super(_io);
                    this._parent = _parent;
                    this._root = _root;
                }
                public void _read() {
                    this.data = this._io.readBytes(_root().header().headerMain().geometry().sectorSize());
                    _dirty = false;
                }

                public void _fetchInstances() {
                }

                public void _write_Seq() {
                    _assertNotDirty();
                    this._io.writeBytes(this.data);
                }

                public void _check() {
                    if (this.data.length != _root().header().headerMain().geometry().sectorSize())
                        throw new ConsistencyError("data", _root().header().headerMain().geometry().sectorSize(), this.data.length);
                    _dirty = false;
                }
                private byte[] data;
                private Vdi _root;
                private Vdi.Disk.Block _parent;
                public byte[] data() { return data; }
                public void setData(byte[] _v) { _dirty = true; data = _v; }
                public Vdi _root() { return _root; }
                public void set_root(Vdi _v) { _dirty = true; _root = _v; }
                public Vdi.Disk.Block _parent() { return _parent; }
                public void set_parent(Vdi.Disk.Block _v) { _dirty = true; _parent = _v; }
            }
            private byte[] metadata;
            private List<Sector> data;
            private Vdi _root;
            private Vdi.Disk _parent;
            private List<byte[]> _raw_data;
            public byte[] metadata() { return metadata; }
            public void setMetadata(byte[] _v) { _dirty = true; metadata = _v; }
            public List<Sector> data() { return data; }
            public void setData(List<Sector> _v) { _dirty = true; data = _v; }
            public Vdi _root() { return _root; }
            public void set_root(Vdi _v) { _dirty = true; _root = _v; }
            public Vdi.Disk _parent() { return _parent; }
            public void set_parent(Vdi.Disk _v) { _dirty = true; _parent = _v; }
            public List<byte[]> _raw_data() { return _raw_data; }
            public void set_raw_Data(List<byte[]> _v) { _dirty = true; _raw_data = _v; }
        }
        private List<Block> blocks;
        private Vdi _root;
        private Vdi _parent;
        public List<Block> blocks() { return blocks; }
        public void setBlocks(List<Block> _v) { _dirty = true; blocks = _v; }
        public Vdi _root() { return _root; }
        public void set_root(Vdi _v) { _dirty = true; _root = _v; }
        public Vdi _parent() { return _parent; }
        public void set_parent(Vdi _v) { _dirty = true; _parent = _v; }
    }
    public static class Header extends KaitaiStruct.ReadWrite {
        public static Header fromFile(String fileName) throws IOException {
            return new Header(new ByteBufferKaitaiStream(fileName));
        }
        public Header() {
            this(null, null, null);
        }

        public Header(KaitaiStream _io) {
            this(_io, null, null);
        }

        public Header(KaitaiStream _io, Vdi _parent) {
            this(_io, _parent, null);
        }

        public Header(KaitaiStream _io, Vdi _parent, Vdi _root) {
            super(_io);
            this._parent = _parent;
            this._root = _root;
        }
        public void _read() {
            this.text = new String(this._io.readBytes(64), StandardCharsets.UTF_8);
            this.signature = this._io.readBytes(4);
            if (!(Arrays.equals(this.signature, new byte[] { 127, 16, -38, -66 }))) {
                throw new KaitaiStream.ValidationNotEqualError(new byte[] { 127, 16, -38, -66 }, this.signature, this._io, "/types/header/seq/1");
            }
            this.version = new Version(this._io, this, _root);
            this.version._read();
            if (subheaderSizeIsDynamic()) {
                this.headerSizeOptional = this._io.readU4le();
            }
            this._raw_headerMain = this._io.readBytes(headerSize());
            KaitaiStream _io__raw_headerMain = new ByteBufferKaitaiStream(this._raw_headerMain);
            this.headerMain = new HeaderMain(_io__raw_headerMain, this, _root);
            this.headerMain._read();
            _dirty = false;
        }

        public void _fetchInstances() {
            this.version._fetchInstances();
            if (subheaderSizeIsDynamic()) {
            }
            this.headerMain._fetchInstances();
        }

        public void _write_Seq() {
            _assertNotDirty();
            this._io.writeBytes((this.text).getBytes(Charset.forName("UTF-8")));
            this._io.writeBytes(this.signature);
            this.version._write_Seq(this._io);
            if (subheaderSizeIsDynamic()) {
                this._io.writeU4le(this.headerSizeOptional);
            }
            final KaitaiStream _io__raw_headerMain = new ByteBufferKaitaiStream(headerSize());
            this._io.addChildStream(_io__raw_headerMain);
            {
                long _pos2 = this._io.pos();
                this._io.seek(this._io.pos() + (headerSize()));
                final Header _this = this;
                _io__raw_headerMain.setWriteBackHandler(new KaitaiStream.WriteBackHandler(_pos2) {
                    @Override
                    protected void write(KaitaiStream parent) {
                        _this._raw_headerMain = _io__raw_headerMain.toByteArray();
                        if (_this._raw_headerMain.length != headerSize())
                            throw new ConsistencyError("raw(header_main)", headerSize(), _this._raw_headerMain.length);
                        parent.writeBytes(_this._raw_headerMain);
                    }
                });
            }
            this.headerMain._write_Seq(_io__raw_headerMain);
        }

        public void _check() {
            if ((this.text).getBytes(Charset.forName("UTF-8")).length != 64)
                throw new ConsistencyError("text", 64, (this.text).getBytes(Charset.forName("UTF-8")).length);
            if (this.signature.length != 4)
                throw new ConsistencyError("signature", 4, this.signature.length);
            if (!(Arrays.equals(this.signature, new byte[] { 127, 16, -38, -66 }))) {
                throw new KaitaiStream.ValidationNotEqualError(new byte[] { 127, 16, -38, -66 }, this.signature, null, "/types/header/seq/1");
            }
            if (!Objects.equals(this.version._root(), _root()))
                throw new ConsistencyError("version", _root(), this.version._root());
            if (!Objects.equals(this.version._parent(), this))
                throw new ConsistencyError("version", this, this.version._parent());
            if (subheaderSizeIsDynamic()) {
            }
            if (!Objects.equals(this.headerMain._root(), _root()))
                throw new ConsistencyError("header_main", _root(), this.headerMain._root());
            if (!Objects.equals(this.headerMain._parent(), this))
                throw new ConsistencyError("header_main", this, this.headerMain._parent());
            _dirty = false;
        }
        public static class HeaderMain extends KaitaiStruct.ReadWrite {
            public static HeaderMain fromFile(String fileName) throws IOException {
                return new HeaderMain(new ByteBufferKaitaiStream(fileName));
            }
            public HeaderMain() {
                this(null, null, null);
            }

            public HeaderMain(KaitaiStream _io) {
                this(_io, null, null);
            }

            public HeaderMain(KaitaiStream _io, Vdi.Header _parent) {
                this(_io, _parent, null);
            }

            public HeaderMain(KaitaiStream _io, Vdi.Header _parent, Vdi _root) {
                super(_io);
                this._parent = _parent;
                this._root = _root;
            }
            public void _read() {
                this.imageType = Vdi.ImageType.byId(this._io.readU4le());
                this.imageFlags = new Flags(this._io, this, _root);
                this.imageFlags._read();
                this.description = new String(this._io.readBytes(256), StandardCharsets.UTF_8);
                if (_parent().version().major() >= 1) {
                    this.blocksMapOffset = this._io.readU4le();
                }
                if (_parent().version().major() >= 1) {
                    this.offsetData = this._io.readU4le();
                }
                this.geometry = new Geometry(this._io, this, _root);
                this.geometry._read();
                if (_parent().version().major() >= 1) {
                    this.reserved1 = this._io.readU4le();
                }
                this.diskSize = this._io.readU8le();
                this.blockDataSize = this._io.readU4le();
                if (_parent().version().major() >= 1) {
                    this.blockMetadataSize = this._io.readU4le();
                }
                this.blocksInImage = this._io.readU4le();
                this.blocksAllocated = this._io.readU4le();
                this.uuidImage = new Uuid(this._io, this, _root);
                this.uuidImage._read();
                this.uuidLastSnap = new Uuid(this._io, this, _root);
                this.uuidLastSnap._read();
                this.uuidLink = new Uuid(this._io, this, _root);
                this.uuidLink._read();
                if (_parent().version().major() >= 1) {
                    this.uuidParent = new Uuid(this._io, this, _root);
                    this.uuidParent._read();
                }
                if ( ((_parent().version().major() >= 1) && (_io().pos() + 16 <= _io().size())) ) {
                    this.lchcGeometry = new Geometry(this._io, this, _root);
                    this.lchcGeometry._read();
                }
                _dirty = false;
            }

            public void _fetchInstances() {
                this.imageFlags._fetchInstances();
                if (_parent().version().major() >= 1) {
                }
                if (_parent().version().major() >= 1) {
                }
                this.geometry._fetchInstances();
                if (_parent().version().major() >= 1) {
                }
                if (_parent().version().major() >= 1) {
                }
                this.uuidImage._fetchInstances();
                this.uuidLastSnap._fetchInstances();
                this.uuidLink._fetchInstances();
                if (_parent().version().major() >= 1) {
                    this.uuidParent._fetchInstances();
                }
                if ( ((_parent().version().major() >= 1) && (_io().pos() + 16 <= _io().size())) ) {
                    this.lchcGeometry._fetchInstances();
                }
            }

            public void _write_Seq() {
                _assertNotDirty();
                this._io.writeU4le(((Number) (this.imageType.id())).longValue());
                this.imageFlags._write_Seq(this._io);
                this._io.writeBytes((this.description).getBytes(Charset.forName("UTF-8")));
                if (_parent().version().major() >= 1) {
                    this._io.writeU4le(this.blocksMapOffset);
                }
                if (_parent().version().major() >= 1) {
                    this._io.writeU4le(this.offsetData);
                }
                this.geometry._write_Seq(this._io);
                if (_parent().version().major() >= 1) {
                    this._io.writeU4le(this.reserved1);
                }
                this._io.writeU8le(this.diskSize);
                this._io.writeU4le(this.blockDataSize);
                if (_parent().version().major() >= 1) {
                    this._io.writeU4le(this.blockMetadataSize);
                }
                this._io.writeU4le(this.blocksInImage);
                this._io.writeU4le(this.blocksAllocated);
                this.uuidImage._write_Seq(this._io);
                this.uuidLastSnap._write_Seq(this._io);
                this.uuidLink._write_Seq(this._io);
                if (_parent().version().major() >= 1) {
                    this.uuidParent._write_Seq(this._io);
                }
                if ( ((_parent().version().major() >= 1) && (_io().pos() + 16 <= _io().size())) ) {
                    if (!Objects.equals(this.lchcGeometry._root(), _root()))
                        throw new ConsistencyError("lchc_geometry", _root(), this.lchcGeometry._root());
                    if (!Objects.equals(this.lchcGeometry._parent(), this))
                        throw new ConsistencyError("lchc_geometry", this, this.lchcGeometry._parent());
                    this.lchcGeometry._write_Seq(this._io);
                }
            }

            public void _check() {
                if (!Objects.equals(this.imageFlags._root(), _root()))
                    throw new ConsistencyError("image_flags", _root(), this.imageFlags._root());
                if (!Objects.equals(this.imageFlags._parent(), this))
                    throw new ConsistencyError("image_flags", this, this.imageFlags._parent());
                if ((this.description).getBytes(Charset.forName("UTF-8")).length != 256)
                    throw new ConsistencyError("description", 256, (this.description).getBytes(Charset.forName("UTF-8")).length);
                if (_parent().version().major() >= 1) {
                }
                if (_parent().version().major() >= 1) {
                }
                if (!Objects.equals(this.geometry._root(), _root()))
                    throw new ConsistencyError("geometry", _root(), this.geometry._root());
                if (!Objects.equals(this.geometry._parent(), this))
                    throw new ConsistencyError("geometry", this, this.geometry._parent());
                if (_parent().version().major() >= 1) {
                }
                if (_parent().version().major() >= 1) {
                }
                if (!Objects.equals(this.uuidImage._root(), _root()))
                    throw new ConsistencyError("uuid_image", _root(), this.uuidImage._root());
                if (!Objects.equals(this.uuidImage._parent(), this))
                    throw new ConsistencyError("uuid_image", this, this.uuidImage._parent());
                if (!Objects.equals(this.uuidLastSnap._root(), _root()))
                    throw new ConsistencyError("uuid_last_snap", _root(), this.uuidLastSnap._root());
                if (!Objects.equals(this.uuidLastSnap._parent(), this))
                    throw new ConsistencyError("uuid_last_snap", this, this.uuidLastSnap._parent());
                if (!Objects.equals(this.uuidLink._root(), _root()))
                    throw new ConsistencyError("uuid_link", _root(), this.uuidLink._root());
                if (!Objects.equals(this.uuidLink._parent(), this))
                    throw new ConsistencyError("uuid_link", this, this.uuidLink._parent());
                if (_parent().version().major() >= 1) {
                    if (!Objects.equals(this.uuidParent._root(), _root()))
                        throw new ConsistencyError("uuid_parent", _root(), this.uuidParent._root());
                    if (!Objects.equals(this.uuidParent._parent(), this))
                        throw new ConsistencyError("uuid_parent", this, this.uuidParent._parent());
                }
                _dirty = false;
            }
            public static class Flags extends KaitaiStruct.ReadWrite {
                public static Flags fromFile(String fileName) throws IOException {
                    return new Flags(new ByteBufferKaitaiStream(fileName));
                }
                public Flags() {
                    this(null, null, null);
                }

                public Flags(KaitaiStream _io) {
                    this(_io, null, null);
                }

                public Flags(KaitaiStream _io, Vdi.Header.HeaderMain _parent) {
                    this(_io, _parent, null);
                }

                public Flags(KaitaiStream _io, Vdi.Header.HeaderMain _parent, Vdi _root) {
                    super(_io);
                    this._parent = _parent;
                    this._root = _root;
                }
                public void _read() {
                    this.reserved0 = this._io.readBitsIntBe(15);
                    this.zeroExpand = this._io.readBitsIntBe(1) != 0;
                    this.reserved1 = this._io.readBitsIntBe(6);
                    this.diff = this._io.readBitsIntBe(1) != 0;
                    this.fixed = this._io.readBitsIntBe(1) != 0;
                    this.reserved2 = this._io.readBitsIntBe(8);
                    _dirty = false;
                }

                public void _fetchInstances() {
                }

                public void _write_Seq() {
                    _assertNotDirty();
                    this._io.writeBitsIntBe(15, this.reserved0);
                    this._io.writeBitsIntBe(1, (this.zeroExpand ? 1 : 0));
                    this._io.writeBitsIntBe(6, this.reserved1);
                    this._io.writeBitsIntBe(1, (this.diff ? 1 : 0));
                    this._io.writeBitsIntBe(1, (this.fixed ? 1 : 0));
                    this._io.writeBitsIntBe(8, this.reserved2);
                }

                public void _check() {
                    _dirty = false;
                }
                private long reserved0;
                private boolean zeroExpand;
                private long reserved1;
                private boolean diff;
                private boolean fixed;
                private long reserved2;
                private Vdi _root;
                private Vdi.Header.HeaderMain _parent;
                public long reserved0() { return reserved0; }
                public void setReserved0(long _v) { _dirty = true; reserved0 = _v; }
                public boolean zeroExpand() { return zeroExpand; }
                public void setZeroExpand(boolean _v) { _dirty = true; zeroExpand = _v; }
                public long reserved1() { return reserved1; }
                public void setReserved1(long _v) { _dirty = true; reserved1 = _v; }
                public boolean diff() { return diff; }
                public void setDiff(boolean _v) { _dirty = true; diff = _v; }
                public boolean fixed() { return fixed; }
                public void setFixed(boolean _v) { _dirty = true; fixed = _v; }
                public long reserved2() { return reserved2; }
                public void setReserved2(long _v) { _dirty = true; reserved2 = _v; }
                public Vdi _root() { return _root; }
                public void set_root(Vdi _v) { _dirty = true; _root = _v; }
                public Vdi.Header.HeaderMain _parent() { return _parent; }
                public void set_parent(Vdi.Header.HeaderMain _v) { _dirty = true; _parent = _v; }
            }
            public static class Geometry extends KaitaiStruct.ReadWrite {
                public static Geometry fromFile(String fileName) throws IOException {
                    return new Geometry(new ByteBufferKaitaiStream(fileName));
                }
                public Geometry() {
                    this(null, null, null);
                }

                public Geometry(KaitaiStream _io) {
                    this(_io, null, null);
                }

                public Geometry(KaitaiStream _io, Vdi.Header.HeaderMain _parent) {
                    this(_io, _parent, null);
                }

                public Geometry(KaitaiStream _io, Vdi.Header.HeaderMain _parent, Vdi _root) {
                    super(_io);
                    this._parent = _parent;
                    this._root = _root;
                }
                public void _read() {
                    this.cylinders = this._io.readU4le();
                    this.heads = this._io.readU4le();
                    this.sectors = this._io.readU4le();
                    this.sectorSize = this._io.readU4le();
                    _dirty = false;
                }

                public void _fetchInstances() {
                }

                public void _write_Seq() {
                    _assertNotDirty();
                    this._io.writeU4le(this.cylinders);
                    this._io.writeU4le(this.heads);
                    this._io.writeU4le(this.sectors);
                    this._io.writeU4le(this.sectorSize);
                }

                public void _check() {
                    _dirty = false;
                }
                private long cylinders;
                private long heads;
                private long sectors;
                private long sectorSize;
                private Vdi _root;
                private Vdi.Header.HeaderMain _parent;
                public long cylinders() { return cylinders; }
                public void setCylinders(long _v) { _dirty = true; cylinders = _v; }
                public long heads() { return heads; }
                public void setHeads(long _v) { _dirty = true; heads = _v; }
                public long sectors() { return sectors; }
                public void setSectors(long _v) { _dirty = true; sectors = _v; }
                public long sectorSize() { return sectorSize; }
                public void setSectorSize(long _v) { _dirty = true; sectorSize = _v; }
                public Vdi _root() { return _root; }
                public void set_root(Vdi _v) { _dirty = true; _root = _v; }
                public Vdi.Header.HeaderMain _parent() { return _parent; }
                public void set_parent(Vdi.Header.HeaderMain _v) { _dirty = true; _parent = _v; }
            }
            private ImageType imageType;
            private Flags imageFlags;
            private String description;
            private Long blocksMapOffset;
            private Long offsetData;
            private Geometry geometry;
            private Long reserved1;
            private long diskSize;
            private long blockDataSize;
            private Long blockMetadataSize;
            private long blocksInImage;
            private long blocksAllocated;
            private Uuid uuidImage;
            private Uuid uuidLastSnap;
            private Uuid uuidLink;
            private Uuid uuidParent;
            private Geometry lchcGeometry;
            private Vdi _root;
            private Vdi.Header _parent;
            public ImageType imageType() { return imageType; }
            public void setImageType(ImageType _v) { _dirty = true; imageType = _v; }
            public Flags imageFlags() { return imageFlags; }
            public void setImageFlags(Flags _v) { _dirty = true; imageFlags = _v; }
            public String description() { return description; }
            public void setDescription(String _v) { _dirty = true; description = _v; }
            public Long blocksMapOffset() { return blocksMapOffset; }
            public void setBlocksMapOffset(Long _v) { _dirty = true; blocksMapOffset = _v; }
            public Long offsetData() { return offsetData; }
            public void setOffsetData(Long _v) { _dirty = true; offsetData = _v; }
            public Geometry geometry() { return geometry; }
            public void setGeometry(Geometry _v) { _dirty = true; geometry = _v; }
            public Long reserved1() { return reserved1; }
            public void setReserved1(Long _v) { _dirty = true; reserved1 = _v; }
            public long diskSize() { return diskSize; }
            public void setDiskSize(long _v) { _dirty = true; diskSize = _v; }

            /**
             * Size of block (bytes).
             */
            public long blockDataSize() { return blockDataSize; }
            public void setBlockDataSize(long _v) { _dirty = true; blockDataSize = _v; }
            public Long blockMetadataSize() { return blockMetadataSize; }
            public void setBlockMetadataSize(Long _v) { _dirty = true; blockMetadataSize = _v; }
            public long blocksInImage() { return blocksInImage; }
            public void setBlocksInImage(long _v) { _dirty = true; blocksInImage = _v; }
            public long blocksAllocated() { return blocksAllocated; }
            public void setBlocksAllocated(long _v) { _dirty = true; blocksAllocated = _v; }
            public Uuid uuidImage() { return uuidImage; }
            public void setUuidImage(Uuid _v) { _dirty = true; uuidImage = _v; }
            public Uuid uuidLastSnap() { return uuidLastSnap; }
            public void setUuidLastSnap(Uuid _v) { _dirty = true; uuidLastSnap = _v; }
            public Uuid uuidLink() { return uuidLink; }
            public void setUuidLink(Uuid _v) { _dirty = true; uuidLink = _v; }
            public Uuid uuidParent() { return uuidParent; }
            public void setUuidParent(Uuid _v) { _dirty = true; uuidParent = _v; }
            public Geometry lchcGeometry() { return lchcGeometry; }
            public void setLchcGeometry(Geometry _v) { _dirty = true; lchcGeometry = _v; }
            public Vdi _root() { return _root; }
            public void set_root(Vdi _v) { _dirty = true; _root = _v; }
            public Vdi.Header _parent() { return _parent; }
            public void set_parent(Vdi.Header _v) { _dirty = true; _parent = _v; }
        }
        public static class Uuid extends KaitaiStruct.ReadWrite {
            public static Uuid fromFile(String fileName) throws IOException {
                return new Uuid(new ByteBufferKaitaiStream(fileName));
            }
            public Uuid() {
                this(null, null, null);
            }

            public Uuid(KaitaiStream _io) {
                this(_io, null, null);
            }

            public Uuid(KaitaiStream _io, Vdi.Header.HeaderMain _parent) {
                this(_io, _parent, null);
            }

            public Uuid(KaitaiStream _io, Vdi.Header.HeaderMain _parent, Vdi _root) {
                super(_io);
                this._parent = _parent;
                this._root = _root;
            }
            public void _read() {
                this.uuid = this._io.readBytes(16);
                _dirty = false;
            }

            public void _fetchInstances() {
            }

            public void _write_Seq() {
                _assertNotDirty();
                this._io.writeBytes(this.uuid);
            }

            public void _check() {
                if (this.uuid.length != 16)
                    throw new ConsistencyError("uuid", 16, this.uuid.length);
                _dirty = false;
            }
            private byte[] uuid;
            private Vdi _root;
            private Vdi.Header.HeaderMain _parent;
            public byte[] uuid() { return uuid; }
            public void setUuid(byte[] _v) { _dirty = true; uuid = _v; }
            public Vdi _root() { return _root; }
            public void set_root(Vdi _v) { _dirty = true; _root = _v; }
            public Vdi.Header.HeaderMain _parent() { return _parent; }
            public void set_parent(Vdi.Header.HeaderMain _v) { _dirty = true; _parent = _v; }
        }
        public static class Version extends KaitaiStruct.ReadWrite {
            public static Version fromFile(String fileName) throws IOException {
                return new Version(new ByteBufferKaitaiStream(fileName));
            }
            public Version() {
                this(null, null, null);
            }

            public Version(KaitaiStream _io) {
                this(_io, null, null);
            }

            public Version(KaitaiStream _io, Vdi.Header _parent) {
                this(_io, _parent, null);
            }

            public Version(KaitaiStream _io, Vdi.Header _parent, Vdi _root) {
                super(_io);
                this._parent = _parent;
                this._root = _root;
            }
            public void _read() {
                this.major = this._io.readU2le();
                this.minor = this._io.readU2le();
                _dirty = false;
            }

            public void _fetchInstances() {
            }

            public void _write_Seq() {
                _assertNotDirty();
                this._io.writeU2le(this.major);
                this._io.writeU2le(this.minor);
            }

            public void _check() {
                _dirty = false;
            }
            private int major;
            private int minor;
            private Vdi _root;
            private Vdi.Header _parent;
            public int major() { return major; }
            public void setMajor(int _v) { _dirty = true; major = _v; }
            public int minor() { return minor; }
            public void setMinor(int _v) { _dirty = true; minor = _v; }
            public Vdi _root() { return _root; }
            public void set_root(Vdi _v) { _dirty = true; _root = _v; }
            public Vdi.Header _parent() { return _parent; }
            public void set_parent(Vdi.Header _v) { _dirty = true; _parent = _v; }
        }
        private Integer blockSize;
        public Integer blockSize() {
            if (this.blockSize != null)
                return this.blockSize;
            this.blockSize = ((Number) (headerMain().blockMetadataSize() + headerMain().blockDataSize())).intValue();
            return this.blockSize;
        }
        public void _invalidateBlockSize() { this.blockSize = null; }
        private Long blocksMapOffset;
        public Long blocksMapOffset() {
            if (this.blocksMapOffset != null)
                return this.blocksMapOffset;
            this.blocksMapOffset = ((Number) (headerMain().blocksMapOffset())).longValue();
            return this.blocksMapOffset;
        }
        public void _invalidateBlocksMapOffset() { this.blocksMapOffset = null; }
        private Integer blocksMapSize;
        public Integer blocksMapSize() {
            if (this.blocksMapSize != null)
                return this.blocksMapSize;
            this.blocksMapSize = ((Number) ((((headerMain().blocksInImage() * 4 + headerMain().geometry().sectorSize()) - 1) / headerMain().geometry().sectorSize()) * headerMain().geometry().sectorSize())).intValue();
            return this.blocksMapSize;
        }
        public void _invalidateBlocksMapSize() { this.blocksMapSize = null; }
        private Long blocksOffset;
        public Long blocksOffset() {
            if (this.blocksOffset != null)
                return this.blocksOffset;
            this.blocksOffset = ((Number) (headerMain().offsetData())).longValue();
            return this.blocksOffset;
        }
        public void _invalidateBlocksOffset() { this.blocksOffset = null; }
        private Integer headerSize;
        public Integer headerSize() {
            if (this.headerSize != null)
                return this.headerSize;
            this.headerSize = ((Number) ((subheaderSizeIsDynamic() ? headerSizeOptional() : 336))).intValue();
            return this.headerSize;
        }
        public void _invalidateHeaderSize() { this.headerSize = null; }
        private Boolean subheaderSizeIsDynamic;
        public Boolean subheaderSizeIsDynamic() {
            if (this.subheaderSizeIsDynamic != null)
                return this.subheaderSizeIsDynamic;
            this.subheaderSizeIsDynamic = version().major() >= 1;
            return this.subheaderSizeIsDynamic;
        }
        public void _invalidateSubheaderSizeIsDynamic() { this.subheaderSizeIsDynamic = null; }
        private String text;
        private byte[] signature;
        private Version version;
        private Long headerSizeOptional;
        private HeaderMain headerMain;
        private Vdi _root;
        private Vdi _parent;
        private byte[] _raw_headerMain;
        public String text() { return text; }
        public void setText(String _v) { _dirty = true; text = _v; }
        public byte[] signature() { return signature; }
        public void setSignature(byte[] _v) { _dirty = true; signature = _v; }
        public Version version() { return version; }
        public void setVersion(Version _v) { _dirty = true; version = _v; }
        public Long headerSizeOptional() { return headerSizeOptional; }
        public void setHeaderSizeOptional(Long _v) { _dirty = true; headerSizeOptional = _v; }
        public HeaderMain headerMain() { return headerMain; }
        public void setHeaderMain(HeaderMain _v) { _dirty = true; headerMain = _v; }
        public Vdi _root() { return _root; }
        public void set_root(Vdi _v) { _dirty = true; _root = _v; }
        public Vdi _parent() { return _parent; }
        public void set_parent(Vdi _v) { _dirty = true; _parent = _v; }
        public byte[] _raw_headerMain() { return _raw_headerMain; }
        public void set_raw_HeaderMain(byte[] _v) { _dirty = true; _raw_headerMain = _v; }
    }
    private Integer blockDiscarded;
    public Integer blockDiscarded() {
        if (this.blockDiscarded != null)
            return this.blockDiscarded;
        this.blockDiscarded = ((int) 4294967294L);
        return this.blockDiscarded;
    }
    public void _invalidateBlockDiscarded() { this.blockDiscarded = null; }
    private Integer blockUnallocated;
    public Integer blockUnallocated() {
        if (this.blockUnallocated != null)
            return this.blockUnallocated;
        this.blockUnallocated = ((int) 4294967295L);
        return this.blockUnallocated;
    }
    public void _invalidateBlockUnallocated() { this.blockUnallocated = null; }
    private BlocksMap blocksMap;
    private boolean _shouldWriteBlocksMap = false;
    private boolean _enabledBlocksMap = true;

    /**
     * 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.
     */
    public BlocksMap blocksMap() {
        if (_shouldWriteBlocksMap)
            _writeBlocksMap();
        if (this.blocksMap != null)
            return this.blocksMap;
        if (!_enabledBlocksMap)
            return null;
        long _pos = this._io.pos();
        this._io.seek(header().blocksMapOffset());
        this._raw_blocksMap = this._io.readBytes(header().blocksMapSize());
        KaitaiStream _io__raw_blocksMap = new ByteBufferKaitaiStream(this._raw_blocksMap);
        this.blocksMap = new BlocksMap(_io__raw_blocksMap, this, _root);
        this.blocksMap._read();
        this._io.seek(_pos);
        return this.blocksMap;
    }
    public void setBlocksMap(BlocksMap _v) { _dirty = true; blocksMap = _v; }
    public void setBlocksMap_Enabled(boolean _v) { _dirty = true; _enabledBlocksMap = _v; }

    private void _writeBlocksMap() {
        _shouldWriteBlocksMap = false;
        long _pos = this._io.pos();
        this._io.seek(header().blocksMapOffset());
        final KaitaiStream _io__raw_blocksMap = new ByteBufferKaitaiStream(header().blocksMapSize());
        this._io.addChildStream(_io__raw_blocksMap);
        {
            long _pos2 = this._io.pos();
            this._io.seek(this._io.pos() + (header().blocksMapSize()));
            final Vdi _this = this;
            _io__raw_blocksMap.setWriteBackHandler(new KaitaiStream.WriteBackHandler(_pos2) {
                @Override
                protected void write(KaitaiStream parent) {
                    _this._raw_blocksMap = _io__raw_blocksMap.toByteArray();
                    if (_this._raw_blocksMap.length != header().blocksMapSize())
                        throw new ConsistencyError("raw(blocks_map)", header().blocksMapSize(), _this._raw_blocksMap.length);
                    parent.writeBytes(_this._raw_blocksMap);
                }
            });
        }
        this.blocksMap._write_Seq(_io__raw_blocksMap);
        this._io.seek(_pos);
    }
    private Disk disk;
    private boolean _shouldWriteDisk = false;
    private boolean _enabledDisk = true;
    public Disk disk() {
        if (_shouldWriteDisk)
            _writeDisk();
        if (this.disk != null)
            return this.disk;
        if (!_enabledDisk)
            return null;
        long _pos = this._io.pos();
        this._io.seek(header().blocksOffset());
        this.disk = new Disk(this._io, this, _root);
        this.disk._read();
        this._io.seek(_pos);
        return this.disk;
    }
    public void setDisk(Disk _v) { _dirty = true; disk = _v; }
    public void setDisk_Enabled(boolean _v) { _dirty = true; _enabledDisk = _v; }

    private void _writeDisk() {
        _shouldWriteDisk = false;
        long _pos = this._io.pos();
        this._io.seek(header().blocksOffset());
        this.disk._write_Seq(this._io);
        this._io.seek(_pos);
    }
    private Header header;
    private Vdi _root;
    private KaitaiStruct.ReadWrite _parent;
    private byte[] _raw_blocksMap;
    public Header header() { return header; }
    public void setHeader(Header _v) { _dirty = true; header = _v; }
    public Vdi _root() { return _root; }
    public void set_root(Vdi _v) { _dirty = true; _root = _v; }
    public KaitaiStruct.ReadWrite _parent() { return _parent; }
    public void set_parent(KaitaiStruct.ReadWrite _v) { _dirty = true; _parent = _v; }
    public byte[] _raw_blocksMap() { return _raw_blocksMap; }
    public void set_raw_BlocksMap(byte[] _v) { _dirty = true; _raw_blocksMap = _v; }
}