summaryrefslogblamecommitdiffstats
path: root/src/main/java/org/openslx/virtualization/disk/DiskImageVmdk.java
blob: 77986ef0fc4b51c53485d2938481c8e48522424e (plain) (tree)
1
2
3
4
5
6
7
8
9
                                        
 
                                
                                         

                             
                                                                                            
                                          
                                                                                     


























                                                                                             
                                                                             






                                                                                            
           
                                                                             





































                                                                                           
                                                                                                    





















                                                                                            
                                                                                                         

                                                                      
                                                                             








                                                                                                                      

                                                                                                                                  





                                                                                                                      
                                                                                                                  







                                                                                                                         

                                                                                                                                         














                                                                                        
                                                                             











                                                                                                     

                                                                                                    
           
                                                               
         
                                                                                                    
                                        






                                                                                                                        
                                                                                                  


                                                                                                               
                                                                                                  



















































                                                                                                           
                                                                                                    






















                                                                                                                         
                                                             



                                                                                                      
                                                                                  













                                                                
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;
	}
}