VMWare Virtual Disk: Nim 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.

Nim source code to parse VMWare Virtual Disk

vmware_vmdk.nim

import kaitai_struct_nim_runtime
import options

type
  VmwareVmdk* = ref object of KaitaiStruct
    `magic`*: seq[byte]
    `version`*: int32
    `flags`*: VmwareVmdk_HeaderFlags
    `sizeMax`*: int64
    `sizeGrain`*: int64
    `startDescriptor`*: int64
    `sizeDescriptor`*: int64
    `numGrainTableEntries`*: int32
    `startSecondaryGrain`*: int64
    `startPrimaryGrain`*: int64
    `sizeMetadata`*: int64
    `isDirty`*: uint8
    `stuff`*: seq[byte]
    `compressionMethod`*: VmwareVmdk_CompressionMethods
    `parent`*: KaitaiStruct
    `lenSectorInst`: int
    `lenSectorInstFlag`: bool
    `descriptorInst`: seq[byte]
    `descriptorInstFlag`: bool
    `grainPrimaryInst`: seq[byte]
    `grainPrimaryInstFlag`: bool
    `grainSecondaryInst`: seq[byte]
    `grainSecondaryInstFlag`: bool
  VmwareVmdk_CompressionMethods* = enum
    none = 0
    deflate = 1
  VmwareVmdk_HeaderFlags* = ref object of KaitaiStruct
    `reserved1`*: uint64
    `zeroedGrainTableEntry`*: bool
    `useSecondaryGrainDir`*: bool
    `validNewLineDetectionTest`*: bool
    `reserved2`*: uint8
    `reserved3`*: uint64
    `hasMetadata`*: bool
    `hasCompressedGrain`*: bool
    `reserved4`*: uint8
    `parent`*: VmwareVmdk

proc read*(_: typedesc[VmwareVmdk], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): VmwareVmdk
proc read*(_: typedesc[VmwareVmdk_HeaderFlags], io: KaitaiStream, root: KaitaiStruct, parent: VmwareVmdk): VmwareVmdk_HeaderFlags

proc lenSector*(this: VmwareVmdk): int
proc descriptor*(this: VmwareVmdk): seq[byte]
proc grainPrimary*(this: VmwareVmdk): seq[byte]
proc grainSecondary*(this: VmwareVmdk): seq[byte]


##[
@see <a href="https://github.com/libyal/libvmdk/blob/main/documentation/VMWare%20Virtual%20Disk%20Format%20(VMDK).asciidoc#41-file-header">Source</a>
]##
proc read*(_: typedesc[VmwareVmdk], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): VmwareVmdk =
  template this: untyped = result
  this = new(VmwareVmdk)
  let root = if root == nil: cast[VmwareVmdk](this) else: cast[VmwareVmdk](root)
  this.io = io
  this.root = root
  this.parent = parent

  let magicExpr = this.io.readBytes(int(4))
  this.magic = magicExpr
  let versionExpr = this.io.readS4le()
  this.version = versionExpr
  let flagsExpr = VmwareVmdk_HeaderFlags.read(this.io, this.root, this)
  this.flags = flagsExpr

  ##[
  Maximum number of sectors in a given image file (capacity)
  ]##
  let sizeMaxExpr = this.io.readS8le()
  this.sizeMax = sizeMaxExpr
  let sizeGrainExpr = this.io.readS8le()
  this.sizeGrain = sizeGrainExpr

  ##[
  Embedded descriptor file start sector number (0 if not available)
  ]##
  let startDescriptorExpr = this.io.readS8le()
  this.startDescriptor = startDescriptorExpr

  ##[
  Number of sectors that embedded descriptor file occupies
  ]##
  let sizeDescriptorExpr = this.io.readS8le()
  this.sizeDescriptor = sizeDescriptorExpr

  ##[
  Number of grains table entries
  ]##
  let numGrainTableEntriesExpr = this.io.readS4le()
  this.numGrainTableEntries = numGrainTableEntriesExpr

  ##[
  Secondary (backup) grain directory start sector number
  ]##
  let startSecondaryGrainExpr = this.io.readS8le()
  this.startSecondaryGrain = startSecondaryGrainExpr

  ##[
  Primary grain directory start sector number
  ]##
  let startPrimaryGrainExpr = this.io.readS8le()
  this.startPrimaryGrain = startPrimaryGrainExpr
  let sizeMetadataExpr = this.io.readS8le()
  this.sizeMetadata = sizeMetadataExpr
  let isDirtyExpr = this.io.readU1()
  this.isDirty = isDirtyExpr
  let stuffExpr = this.io.readBytes(int(4))
  this.stuff = stuffExpr
  let compressionMethodExpr = VmwareVmdk_CompressionMethods(this.io.readU2le())
  this.compressionMethod = compressionMethodExpr

proc lenSector(this: VmwareVmdk): int = 
  if this.lenSectorInstFlag:
    return this.lenSectorInst
  let lenSectorInstExpr = int(512)
  this.lenSectorInst = lenSectorInstExpr
  this.lenSectorInstFlag = true
  return this.lenSectorInst

proc descriptor(this: VmwareVmdk): seq[byte] = 
  if this.descriptorInstFlag:
    return this.descriptorInst
  let pos = this.io.pos()
  this.io.seek(int((this.startDescriptor * VmwareVmdk(this.root).lenSector)))
  let descriptorInstExpr = this.io.readBytes(int((this.sizeDescriptor * VmwareVmdk(this.root).lenSector)))
  this.descriptorInst = descriptorInstExpr
  this.io.seek(pos)
  this.descriptorInstFlag = true
  return this.descriptorInst

proc grainPrimary(this: VmwareVmdk): seq[byte] = 
  if this.grainPrimaryInstFlag:
    return this.grainPrimaryInst
  let pos = this.io.pos()
  this.io.seek(int((this.startPrimaryGrain * VmwareVmdk(this.root).lenSector)))
  let grainPrimaryInstExpr = this.io.readBytes(int((this.sizeGrain * VmwareVmdk(this.root).lenSector)))
  this.grainPrimaryInst = grainPrimaryInstExpr
  this.io.seek(pos)
  this.grainPrimaryInstFlag = true
  return this.grainPrimaryInst

proc grainSecondary(this: VmwareVmdk): seq[byte] = 
  if this.grainSecondaryInstFlag:
    return this.grainSecondaryInst
  let pos = this.io.pos()
  this.io.seek(int((this.startSecondaryGrain * VmwareVmdk(this.root).lenSector)))
  let grainSecondaryInstExpr = this.io.readBytes(int((this.sizeGrain * VmwareVmdk(this.root).lenSector)))
  this.grainSecondaryInst = grainSecondaryInstExpr
  this.io.seek(pos)
  this.grainSecondaryInstFlag = true
  return this.grainSecondaryInst

proc fromFile*(_: typedesc[VmwareVmdk], filename: string): VmwareVmdk =
  VmwareVmdk.read(newKaitaiFileStream(filename), nil, nil)


##[
@see <a href="https://github.com/libyal/libvmdk/blob/main/documentation/VMWare%20Virtual%20Disk%20Format%20(VMDK).asciidoc#411-flags">Source</a>
]##
proc read*(_: typedesc[VmwareVmdk_HeaderFlags], io: KaitaiStream, root: KaitaiStruct, parent: VmwareVmdk): VmwareVmdk_HeaderFlags =
  template this: untyped = result
  this = new(VmwareVmdk_HeaderFlags)
  let root = if root == nil: cast[VmwareVmdk](this) else: cast[VmwareVmdk](root)
  this.io = io
  this.root = root
  this.parent = parent

  let reserved1Expr = this.io.readBitsIntBe(5)
  this.reserved1 = reserved1Expr
  let zeroedGrainTableEntryExpr = this.io.readBitsIntBe(1) != 0
  this.zeroedGrainTableEntry = zeroedGrainTableEntryExpr
  let useSecondaryGrainDirExpr = this.io.readBitsIntBe(1) != 0
  this.useSecondaryGrainDir = useSecondaryGrainDirExpr
  let validNewLineDetectionTestExpr = this.io.readBitsIntBe(1) != 0
  this.validNewLineDetectionTest = validNewLineDetectionTestExpr
  alignToByte(this.io)
  let reserved2Expr = this.io.readU1()
  this.reserved2 = reserved2Expr
  let reserved3Expr = this.io.readBitsIntBe(6)
  this.reserved3 = reserved3Expr
  let hasMetadataExpr = this.io.readBitsIntBe(1) != 0
  this.hasMetadata = hasMetadataExpr
  let hasCompressedGrainExpr = this.io.readBitsIntBe(1) != 0
  this.hasCompressedGrain = hasCompressedGrainExpr
  alignToByte(this.io)
  let reserved4Expr = this.io.readU1()
  this.reserved4 = reserved4Expr

proc fromFile*(_: typedesc[VmwareVmdk_HeaderFlags], filename: string): VmwareVmdk_HeaderFlags =
  VmwareVmdk_HeaderFlags.read(newKaitaiFileStream(filename), nil, nil)