VMWare Virtual Disk: C++98/STL parsing library

This page hosts a formal specification of VMWare Virtual Disk using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All parsing code for C++98/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.

For C++, the easiest way is to clone the runtime library sources and build them along with your project.

Code

Using Kaitai Struct in C++/STL usually consists of 3 steps.

  1. We need to create an STL input stream (std::istream). One can open local file for that, or use existing std::string or char* buffer.
    #include <fstream>
    
    std::ifstream is("path/to/local/file.vmdk", std::ifstream::binary);
    
    #include <sstream>
    
    std::istringstream is(str);
    
    #include <sstream>
    
    const char buf[] = { ... };
    std::string str(buf, sizeof buf);
    std::istringstream is(str);
    
  2. We need to wrap our input stream into Kaitai stream:
    #include "kaitai/kaitaistream.h"
    
    kaitai::kstream ks(&is);
    
  3. And finally, we can invoke the parsing:
    vmware_vmdk_t data(&ks);
    

After that, one can get various attributes from the structure by invoking getter methods like:

data.size_max() // => Maximum number of sectors in a given image file (capacity)

C++98/STL source code to parse VMWare Virtual Disk

vmware_vmdk.h

#ifndef VMWARE_VMDK_H_
#define VMWARE_VMDK_H_

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

#include "kaitai/kaitaistruct.h"
#include <stdint.h>

#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif

/**
 * \sa https://github.com/libyal/libvmdk/blob/main/documentation/VMWare%20Virtual%20Disk%20Format%20(VMDK).asciidoc#41-file-header Source
 */

class vmware_vmdk_t : public kaitai::kstruct {

public:
    class header_flags_t;

    enum compression_methods_t {
        COMPRESSION_METHODS_NONE = 0,
        COMPRESSION_METHODS_DEFLATE = 1
    };

    vmware_vmdk_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, vmware_vmdk_t* p__root = 0);

private:
    void _read();
    void _clean_up();

public:
    ~vmware_vmdk_t();

    /**
     * \sa https://github.com/libyal/libvmdk/blob/main/documentation/VMWare%20Virtual%20Disk%20Format%20(VMDK).asciidoc#411-flags Source
     */

    class header_flags_t : public kaitai::kstruct {

    public:

        header_flags_t(kaitai::kstream* p__io, vmware_vmdk_t* p__parent = 0, vmware_vmdk_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~header_flags_t();

    private:
        uint64_t m_reserved1;
        bool m_zeroed_grain_table_entry;
        bool m_use_secondary_grain_dir;
        bool m_valid_new_line_detection_test;
        uint8_t m_reserved2;
        uint64_t m_reserved3;
        bool m_has_metadata;
        bool m_has_compressed_grain;
        uint8_t m_reserved4;
        vmware_vmdk_t* m__root;
        vmware_vmdk_t* m__parent;

    public:
        uint64_t reserved1() const { return m_reserved1; }
        bool zeroed_grain_table_entry() const { return m_zeroed_grain_table_entry; }
        bool use_secondary_grain_dir() const { return m_use_secondary_grain_dir; }
        bool valid_new_line_detection_test() const { return m_valid_new_line_detection_test; }
        uint8_t reserved2() const { return m_reserved2; }
        uint64_t reserved3() const { return m_reserved3; }
        bool has_metadata() const { return m_has_metadata; }
        bool has_compressed_grain() const { return m_has_compressed_grain; }
        uint8_t reserved4() const { return m_reserved4; }
        vmware_vmdk_t* _root() const { return m__root; }
        vmware_vmdk_t* _parent() const { return m__parent; }
    };

private:
    bool f_len_sector;
    int32_t m_len_sector;

public:
    int32_t len_sector();

private:
    bool f_descriptor;
    std::string m_descriptor;

public:
    std::string descriptor();

private:
    bool f_grain_primary;
    std::string m_grain_primary;

public:
    std::string grain_primary();

private:
    bool f_grain_secondary;
    std::string m_grain_secondary;

public:
    std::string grain_secondary();

private:
    std::string m_magic;
    int32_t m_version;
    header_flags_t* m_flags;
    int64_t m_size_max;
    int64_t m_size_grain;
    int64_t m_start_descriptor;
    int64_t m_size_descriptor;
    int32_t m_num_grain_table_entries;
    int64_t m_start_secondary_grain;
    int64_t m_start_primary_grain;
    int64_t m_size_metadata;
    uint8_t m_is_dirty;
    std::string m_stuff;
    compression_methods_t m_compression_method;
    vmware_vmdk_t* m__root;
    kaitai::kstruct* m__parent;

public:
    std::string magic() const { return m_magic; }
    int32_t version() const { return m_version; }
    header_flags_t* flags() const { return m_flags; }

    /**
     * Maximum number of sectors in a given image file (capacity)
     */
    int64_t size_max() const { return m_size_max; }
    int64_t size_grain() const { return m_size_grain; }

    /**
     * Embedded descriptor file start sector number (0 if not available)
     */
    int64_t start_descriptor() const { return m_start_descriptor; }

    /**
     * Number of sectors that embedded descriptor file occupies
     */
    int64_t size_descriptor() const { return m_size_descriptor; }

    /**
     * Number of grains table entries
     */
    int32_t num_grain_table_entries() const { return m_num_grain_table_entries; }

    /**
     * Secondary (backup) grain directory start sector number
     */
    int64_t start_secondary_grain() const { return m_start_secondary_grain; }

    /**
     * Primary grain directory start sector number
     */
    int64_t start_primary_grain() const { return m_start_primary_grain; }
    int64_t size_metadata() const { return m_size_metadata; }
    uint8_t is_dirty() const { return m_is_dirty; }
    std::string stuff() const { return m_stuff; }
    compression_methods_t compression_method() const { return m_compression_method; }
    vmware_vmdk_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
};

#endif  // VMWARE_VMDK_H_

vmware_vmdk.cpp

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

#include "vmware_vmdk.h"
#include "kaitai/exceptions.h"

vmware_vmdk_t::vmware_vmdk_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, vmware_vmdk_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_flags = 0;
    f_len_sector = false;
    f_descriptor = false;
    f_grain_primary = false;
    f_grain_secondary = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void vmware_vmdk_t::_read() {
    m_magic = m__io->read_bytes(4);
    if (!(magic() == std::string("\x4B\x44\x4D\x56", 4))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x4B\x44\x4D\x56", 4), magic(), _io(), std::string("/seq/0"));
    }
    m_version = m__io->read_s4le();
    m_flags = new header_flags_t(m__io, this, m__root);
    m_size_max = m__io->read_s8le();
    m_size_grain = m__io->read_s8le();
    m_start_descriptor = m__io->read_s8le();
    m_size_descriptor = m__io->read_s8le();
    m_num_grain_table_entries = m__io->read_s4le();
    m_start_secondary_grain = m__io->read_s8le();
    m_start_primary_grain = m__io->read_s8le();
    m_size_metadata = m__io->read_s8le();
    m_is_dirty = m__io->read_u1();
    m_stuff = m__io->read_bytes(4);
    m_compression_method = static_cast<vmware_vmdk_t::compression_methods_t>(m__io->read_u2le());
}

vmware_vmdk_t::~vmware_vmdk_t() {
    _clean_up();
}

void vmware_vmdk_t::_clean_up() {
    if (m_flags) {
        delete m_flags; m_flags = 0;
    }
    if (f_descriptor) {
    }
    if (f_grain_primary) {
    }
    if (f_grain_secondary) {
    }
}

vmware_vmdk_t::header_flags_t::header_flags_t(kaitai::kstream* p__io, vmware_vmdk_t* p__parent, vmware_vmdk_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void vmware_vmdk_t::header_flags_t::_read() {
    m_reserved1 = m__io->read_bits_int_be(5);
    m_zeroed_grain_table_entry = m__io->read_bits_int_be(1);
    m_use_secondary_grain_dir = m__io->read_bits_int_be(1);
    m_valid_new_line_detection_test = m__io->read_bits_int_be(1);
    m__io->align_to_byte();
    m_reserved2 = m__io->read_u1();
    m_reserved3 = m__io->read_bits_int_be(6);
    m_has_metadata = m__io->read_bits_int_be(1);
    m_has_compressed_grain = m__io->read_bits_int_be(1);
    m__io->align_to_byte();
    m_reserved4 = m__io->read_u1();
}

vmware_vmdk_t::header_flags_t::~header_flags_t() {
    _clean_up();
}

void vmware_vmdk_t::header_flags_t::_clean_up() {
}

int32_t vmware_vmdk_t::len_sector() {
    if (f_len_sector)
        return m_len_sector;
    m_len_sector = 512;
    f_len_sector = true;
    return m_len_sector;
}

std::string vmware_vmdk_t::descriptor() {
    if (f_descriptor)
        return m_descriptor;
    std::streampos _pos = m__io->pos();
    m__io->seek((start_descriptor() * _root()->len_sector()));
    m_descriptor = m__io->read_bytes((size_descriptor() * _root()->len_sector()));
    m__io->seek(_pos);
    f_descriptor = true;
    return m_descriptor;
}

std::string vmware_vmdk_t::grain_primary() {
    if (f_grain_primary)
        return m_grain_primary;
    std::streampos _pos = m__io->pos();
    m__io->seek((start_primary_grain() * _root()->len_sector()));
    m_grain_primary = m__io->read_bytes((size_grain() * _root()->len_sector()));
    m__io->seek(_pos);
    f_grain_primary = true;
    return m_grain_primary;
}

std::string vmware_vmdk_t::grain_secondary() {
    if (f_grain_secondary)
        return m_grain_secondary;
    std::streampos _pos = m__io->pos();
    m__io->seek((start_secondary_grain() * _root()->len_sector()));
    m_grain_secondary = m__io->read_bytes((size_grain() * _root()->len_sector()));
    m__io->seek(_pos);
    f_grain_secondary = true;
    return m_grain_secondary;
}