summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openslx/virtualization/disk/DiskImageVmdk.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/openslx/virtualization/disk/DiskImageVmdk.java')
-rw-r--r--src/main/java/org/openslx/virtualization/disk/DiskImageVmdk.java282
1 files changed, 282 insertions, 0 deletions
diff --git a/src/main/java/org/openslx/virtualization/disk/DiskImageVmdk.java b/src/main/java/org/openslx/virtualization/disk/DiskImageVmdk.java
new file mode 100644
index 0000000..77986ef
--- /dev/null
+++ b/src/main/java/org/openslx/virtualization/disk/DiskImageVmdk.java
@@ -0,0 +1,282 @@
+package org.openslx.virtualization.disk;
+
+import java.io.RandomAccessFile;
+import java.nio.charset.StandardCharsets;
+
+import org.openslx.util.Util;
+import org.openslx.virtualization.configuration.VirtualizationConfigurationVmwareFileFormat;
+import org.openslx.virtualization.Version;
+import org.openslx.virtualization.configuration.VirtualizationConfigurationException;
+
+/**
+ * VMDK (sparse extent) disk image for virtual machines.
+ *
+ * @author Manuel Bentele
+ * @version 1.0
+ */
+public class DiskImageVmdk extends DiskImage
+{
+ /**
+ * Big endian representation of the little endian magic bytes <code>KDMV</code>.
+ */
+ private static final int VMDK_MAGIC = 0x4b444d56;
+
+ /**
+ * Size of a VMDK disk image data cluster in bytes.
+ */
+ private static final int VMDK_SECTOR_SIZE = 512;
+
+ /**
+ * Default hardware version of a VMDK disk image.
+ */
+ private static final int VMDK_DEFAULT_HW_VERSION = 10;
+
+ /**
+ * Stores disk configuration if VMDK disk image contains an embedded descriptor file.
+ */
+ private final VirtualizationConfigurationVmwareFileFormat vmdkConfig;
+
+ /**
+ * Creates a new VMDK disk image from an existing VMDK image file.
+ *
+ * @param diskImage file to a VMDK disk storing the image content.
+ *
+ * @throws DiskImageException parsing of the VMDK's embedded descriptor file failed.
+ */
+ DiskImageVmdk( RandomAccessFile diskImage ) throws DiskImageException
+ {
+ super( diskImage );
+
+ this.vmdkConfig = this.parseVmdkConfig();
+ }
+
+ /**
+ * Probe specified file with unknown format to be a VMDK disk image file.
+ *
+ * @param diskImage file with unknown format that should be probed.
+ * @return state whether file is a VMDK disk image or not.
+ *
+ * @throws DiskImageException cannot probe specified file with unknown format.
+ */
+ public static boolean probe( RandomAccessFile diskImage ) throws DiskImageException
+ {
+ final boolean isVmdkImageFormat;
+
+ // goto the beginning of the disk image to read the magic bytes
+ final int diskImageMagic = DiskImageUtils.readInt( diskImage, 0 );
+
+ // check if disk image's magic bytes can be found
+ if ( diskImageMagic == DiskImageVmdk.VMDK_MAGIC ) {
+ isVmdkImageFormat = true;
+ } else {
+ isVmdkImageFormat = false;
+ }
+
+ return isVmdkImageFormat;
+ }
+
+ /**
+ * Returns the creation type from the VMDK's embedded descriptor file.
+ *
+ * @return creation type from the VMDK's embedded descriptor file.
+ */
+ private String getCreationType()
+ {
+ final VirtualizationConfigurationVmwareFileFormat vmdkConfig = this.getVmdkConfig();
+ final String vmdkCreationType;
+
+ if ( vmdkConfig == null ) {
+ // VMDK disk image does not contain any descriptor file
+ // assume that the file is not stand alone
+ vmdkCreationType = null;
+ } else {
+ // VMDK disk image contains a descriptor file
+ // get creation type from the content of the descriptor file
+ vmdkCreationType = this.vmdkConfig.get( "createType" );
+ }
+
+ return vmdkCreationType;
+ }
+
+ /**
+ * Parse the configuration of the VMDK's embedded descriptor file.
+ *
+ * @return parsed configuration of the VMDK's embedded descriptor file.
+ *
+ * @throws DiskImageException parsing of the VMDK's embedded descriptor file failed.
+ */
+ protected VirtualizationConfigurationVmwareFileFormat parseVmdkConfig() throws DiskImageException
+ {
+ final RandomAccessFile diskFile = this.getDiskImage();
+ final VirtualizationConfigurationVmwareFileFormat vmdkConfig;
+
+ // get offset and size of descriptor file embedded into the VMDK disk image
+ final long vmdkDescriptorSectorOffset = Long.reverseBytes( DiskImageUtils.readLong( diskFile, 28 ) );
+ final long vmdkDescriptorSectorSize = Long.reverseBytes( DiskImageUtils.readLong( diskFile, 36 ) );
+
+ if ( vmdkDescriptorSectorOffset > 0 ) {
+ // get content of descriptor file embedded into the VMDK disk image
+ final long vmdkDescriptorOffset = vmdkDescriptorSectorOffset * DiskImageVmdk.VMDK_SECTOR_SIZE;
+ final long vmdkDescriptorSizeMax = vmdkDescriptorSectorSize * DiskImageVmdk.VMDK_SECTOR_SIZE;
+ final String descriptorStr = new String ( DiskImageUtils.readBytesAsArray( diskFile, vmdkDescriptorOffset,
+ Long.valueOf( vmdkDescriptorSizeMax ).intValue() ), StandardCharsets.US_ASCII );
+
+ // get final length of the content within the sectors to be able to trim all 'zero' characters
+ final int vmdkDescriptorSize = descriptorStr.indexOf( 0 );
+
+ // if final length of the content is invalid, throw an exception
+ if ( vmdkDescriptorSize > vmdkDescriptorSizeMax || vmdkDescriptorSize < 0 ) {
+ final String errorMsg = "Embedded descriptor size in VMDK disk image is invalid!";
+ throw new DiskImageException( errorMsg );
+ }
+
+ // trim all 'zero' characters at the end of the descriptor content to avoid errors during parsing
+ final String configStr = descriptorStr.substring( 0, vmdkDescriptorSize );
+
+ // create configuration instance from content of the descriptor file
+ try {
+ vmdkConfig = new VirtualizationConfigurationVmwareFileFormat( configStr.getBytes(), vmdkDescriptorSize );
+ } catch ( VirtualizationConfigurationException e ) {
+ throw new DiskImageException( e.getLocalizedMessage() );
+ }
+ } else {
+ // there is no descriptor file embedded into the VMDK disk image
+ vmdkConfig = null;
+ }
+
+ return vmdkConfig;
+ }
+
+ /**
+ * Returns parsed configuration of the VMDK's embedded descriptor file.
+ *
+ * @return parsed configuration of the VMDK's embedded descriptor file.
+ */
+ protected VirtualizationConfigurationVmwareFileFormat getVmdkConfig()
+ {
+ return this.vmdkConfig;
+ }
+
+ /**
+ * Returns the hardware version from the VMDK's embedded descriptor file.
+ *
+ * If the VMDK's embedded descriptor file does not contain any hardware version configuration
+ * entry, the default hardware version (see {@link #VMDK_DEFAULT_HW_VERSION}) is returned.
+ *
+ * @return hardware version from the VMDK's embedded descriptor file.
+ *
+ * @throws DiskImageException unable to obtain the VMDK's hardware version of the disk image
+ * format.
+ */
+ public Version getHwVersion() throws DiskImageException
+ {
+ final VirtualizationConfigurationVmwareFileFormat vmdkConfig = this.getVmdkConfig();
+ final Version hwVersion;
+
+ if ( vmdkConfig != null ) {
+ // VMDK image contains a hardware version, so return parsed hardware version
+ // if hardware version cannot be parsed, return default hardware version
+ final String hwVersionStr = vmdkConfig.get( "ddb.virtualHWVersion" );
+
+ final int hwVersionMajor = Util.parseInt( hwVersionStr, DiskImageVmdk.VMDK_DEFAULT_HW_VERSION );
+ hwVersion = new Version( Integer.valueOf( hwVersionMajor ).shortValue() );
+ } else {
+ // VMDK image does not contain any hardware version, so return default hardware version
+ final int hwVersionMajor = DiskImageVmdk.VMDK_DEFAULT_HW_VERSION;
+ hwVersion = new Version( Integer.valueOf( hwVersionMajor ).shortValue() );
+ }
+
+ return hwVersion;
+ }
+
+ @Override
+ public boolean isStandalone() throws DiskImageException
+ {
+ final String vmdkCreationType = this.getCreationType();
+ final boolean vmdkStandalone;
+
+ if ( vmdkCreationType != null ) {
+ // creation type is defined, so check if VMDK disk image is a snapshot
+ if ( this.isSnapshot() ) {
+ // VMDK disk image is a snapshot and not stand alone
+ vmdkStandalone = false;
+ } else {
+ // VMDK disk image is not a snapshot
+ // determine stand alone disk image property
+ vmdkStandalone = vmdkCreationType.equalsIgnoreCase( "streamOptimized" ) ||
+ vmdkCreationType.equalsIgnoreCase( "monolithicSparse" );
+ }
+ } else {
+ // creation type is not defined
+ // assume that the file is not stand alone
+ vmdkStandalone = false;
+ }
+
+ return vmdkStandalone;
+ }
+
+ @Override
+ public boolean isCompressed() throws DiskImageException
+ {
+ final String vmdkCreationType = this.getCreationType();
+ final boolean vmdkCompressed;
+
+ if ( vmdkCreationType != null && vmdkCreationType.equalsIgnoreCase( "streamOptimized" ) ) {
+ // creation type is defined, and VMDK disk image is compressed
+ vmdkCompressed = true;
+ } else {
+ // creation type for compression is not defined
+ // assume that the file is not compressed
+ vmdkCompressed = false;
+ }
+
+ return vmdkCompressed;
+ }
+
+ @Override
+ public boolean isSnapshot() throws DiskImageException
+ {
+ final VirtualizationConfigurationVmwareFileFormat vmdkConfig = this.getVmdkConfig();
+ final boolean vmdkSnapshot;
+
+ if ( vmdkConfig == null ) {
+ // VMDK disk image does not contain any descriptor file
+ // assume that the file is not a snapshot
+ vmdkSnapshot = false;
+ } else {
+ // get parent CID to determine snapshot disk image property
+ final String parentCid = vmdkConfig.get( "parentCID" );
+
+ if ( parentCid != null && !parentCid.equalsIgnoreCase( "ffffffff" ) ) {
+ // link to parent content identifier is defined, so VMDK disk image is a snapshot
+ vmdkSnapshot = true;
+ } else {
+ // link to parent content identifier is not defined, so VMDK disk image is not a snapshot
+ vmdkSnapshot = false;
+ }
+ }
+
+ return vmdkSnapshot;
+ }
+
+ @Override
+ public Version getVersion() throws DiskImageException
+ {
+ final RandomAccessFile diskFile = this.getDiskImage();
+ final int vmdkVersion = Integer.reverseBytes( DiskImageUtils.readInt( diskFile, 4 ) );
+
+ return new Version( Integer.valueOf( vmdkVersion ).shortValue() );
+ }
+
+ @Override
+ public String getDescription() throws DiskImageException
+ {
+ return null;
+ }
+
+ @Override
+ public ImageFormat getFormat()
+ {
+ return ImageFormat.VMDK;
+ }
+}