Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.10</version>
</dependency>
</dependencies>

<build>
Expand Down
85 changes: 73 additions & 12 deletions core/src/com/cloud/storage/template/VhdProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,24 @@

package com.cloud.storage.template;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Map;

import javax.naming.ConfigurationException;

import com.cloud.exception.InternalErrorException;
import org.apache.log4j.Logger;

import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.StorageLayer;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.AdapterBase;
import org.apache.commons.compress.compressors.CompressorException;
import org.apache.commons.compress.compressors.CompressorInputStream;
import org.apache.commons.compress.compressors.CompressorStreamFactory;
import org.apache.log4j.Logger;

import javax.naming.ConfigurationException;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

/**
* VhdProcessor processes the downloaded template for VHD. It
Expand All @@ -45,10 +49,12 @@ public class VhdProcessor extends AdapterBase implements Processor {
private static final Logger s_logger = Logger.getLogger(VhdProcessor.class);
StorageLayer _storage;
private int vhdFooterSize = 512;
private int vhdCookieOffset = 8;
private int vhdFooterCreatorAppOffset = 28;
private int vhdFooterCreatorVerOffset = 32;
private int vhdFooterCurrentSizeOffset = 48;
private byte[][] citrixCreatorApp = { {0x74, 0x61, 0x70, 0x00}, {0x43, 0x54, 0x58, 0x53}}; /*"tap ", and "CTXS"*/
private String vhdIdentifierCookie = "conectix";

@Override
public FormatInfo process(String templatePath, ImageFormat format, String templateName) throws InternalErrorException {
Expand Down Expand Up @@ -93,25 +99,54 @@ public long getVirtualSize(File file) throws IOException {

protected long getTemplateVirtualSize(File file) throws IOException {
byte[] currentSize = new byte[8];
byte[] cookie = new byte[8];
byte[] creatorApp = new byte[4];

try (FileInputStream strm = new FileInputStream(file)) {
long skipped = strm.skip(file.length() - vhdFooterSize + vhdFooterCreatorAppOffset);

BufferedInputStream fileStream = new BufferedInputStream(new FileInputStream(file));
InputStream strm = fileStream;

boolean isCompressed = checkCompressed(file.getAbsolutePath());

if ( isCompressed ) {
try {
strm = new CompressorStreamFactory().createCompressorInputStream(fileStream);
} catch (CompressorException e) {
s_logger.info("error opening compressed VHD file " + file.getName());
return file.length();
}
} try {

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove the unneeded line break here since I already have another change for you to make. :) Thx...

//read the backup footer present at the top of the VHD file
strm.read(cookie);
if (! new String(cookie).equals(vhdIdentifierCookie)) {
strm.close();
return file.length();
}

long skipped = strm.skip(vhdFooterCreatorAppOffset - vhdCookieOffset);
if (skipped == -1) {
throw new IOException("Unexpected end-of-file");
}
long read = strm.read(creatorApp);
if (read == -1) {
throw new IOException("Unexpected end-of-file");
}
skipped = strm.skip(vhdFooterCurrentSizeOffset - vhdFooterCreatorVerOffset);
skipped = strm.skip(vhdFooterCurrentSizeOffset - vhdFooterCreatorVerOffset - vhdCookieOffset);
if (skipped == -1) {
throw new IOException("Unexpected end-of-file");
}
read = strm.read(currentSize);
if (read == -1) {
throw new IOException("Unexpected end-of-file");
}
} catch (IOException e) {
s_logger.warn("Error reading virtual size from VHD file " + e.getMessage() + " VHD: " + file.getName());
return file.length();
} finally {
if (strm != null) {
strm.close();
}
}

return NumbersUtil.bytesToLong(currentSize);
Expand All @@ -128,4 +163,30 @@ public boolean configure(String name, Map<String, Object> params) throws Configu
return true;
}

private boolean checkCompressed(String fileName) throws IOException {

FileInputStream fin = null;
BufferedInputStream bin = null;
CompressorInputStream cin = null;

try {
fin = new FileInputStream(fileName);
bin = new BufferedInputStream(fin);
cin = new CompressorStreamFactory().createCompressorInputStream(bin);

} catch (CompressorException e) {
s_logger.warn(e.getMessage());
return false;

} catch (FileNotFoundException e) {
s_logger.warn(e.getMessage());
return false;
} finally {
if (cin != null)
cin.close();
else if (bin != null)
bin.close();
}
return true;
}
}
36 changes: 35 additions & 1 deletion core/test/com/cloud/storage/template/VhdProcessorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@

import java.io.File;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

Expand Down Expand Up @@ -107,4 +109,36 @@ public void testGetVirtualSize() throws Exception{
Assert.assertEquals(virtualSize, processor.getVirtualSize(mockFile));
Mockito.verify(mockFile,Mockito.times(0)).length();
}
}

@Test
public void testVhdGetVirtualSize() throws Exception {
String vhdPath = URLDecoder.decode(getClass().getResource("/vhds/test.vhd").getFile(), Charset.defaultCharset().name());
long expectedVirtualSizeBytes = 104857600;
long actualVirtualSizeBytes = processor.getVirtualSize(new File(vhdPath));
Assert.assertEquals(expectedVirtualSizeBytes, actualVirtualSizeBytes);
}

@Test
public void testGzipVhdGetVirtualSize() throws Exception {
String gzipVhdPath = URLDecoder.decode(getClass().getResource("/vhds/test.vhd.gz").getFile(), Charset.defaultCharset().name());
long expectedVirtualSizeBytes = 104857600;
long actualVirtualSizeBytes = processor.getVirtualSize(new File(gzipVhdPath));
Assert.assertEquals(expectedVirtualSizeBytes, actualVirtualSizeBytes);
}

@Test
public void testBzip2VhdGetVirtualSize() throws Exception {
String bzipVhdPath = URLDecoder.decode(getClass().getResource("/vhds/test.vhd.bz2").getFile(), Charset.defaultCharset().name());
long expectedVirtualSizeBytes = 104857600;
long actualVirtualSizeBytes = processor.getVirtualSize(new File(bzipVhdPath));
Assert.assertEquals(expectedVirtualSizeBytes, actualVirtualSizeBytes);
}

@Test
public void testZipVhdGetVirtualSize() throws Exception {
String zipVhdPath = URLDecoder.decode(getClass().getResource("/vhds/test.vhd.zip").getFile(), Charset.defaultCharset().name());
long expectedVirtualSizeBytes = 341; //Zip is not a supported format, virtual size should return the filesize as a fallback
long actualVirtualSizeBytes = processor.getVirtualSize(new File(zipVhdPath));
Assert.assertEquals(expectedVirtualSizeBytes, actualVirtualSizeBytes);
}
}
Binary file added core/test/resources/vhds/test.vhd
Binary file not shown.
Binary file added core/test/resources/vhds/test.vhd.bz2
Binary file not shown.
Binary file added core/test/resources/vhds/test.vhd.gz
Binary file not shown.
Binary file added core/test/resources/vhds/test.vhd.zip
Binary file not shown.