blob: baea7bf0db51e78714a1a5565773e90e962a834a [file] [log] [blame]
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
package com.cloud.storage.template;
import com.cloud.exception.InternalErrorException;
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
* currently does not handle any type of template conversion
* into the VHD format.
*
*/
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 {
return process(templatePath, format, templateName, 0);
}
@Override
public FormatInfo process(String templatePath, ImageFormat format, String templateName, long processTimeout) throws InternalErrorException {
if (format != null) {
s_logger.debug("We currently don't handle conversion from " + format + " to VHD.");
return null;
}
String vhdPath = templatePath + File.separator + templateName + "." + ImageFormat.VHD.getFileExtension();
if (!_storage.exists(vhdPath)) {
s_logger.debug("Unable to find the vhd file: " + vhdPath);
return null;
}
File vhdFile = _storage.getFile(vhdPath);
FormatInfo info = new FormatInfo();
info.format = ImageFormat.VHD;
info.filename = templateName + "." + ImageFormat.VHD.getFileExtension();
info.size = _storage.getSize(vhdPath);
try {
info.virtualSize = getTemplateVirtualSize(vhdFile);
} catch (IOException e) {
s_logger.error("Unable to get the virtual size for " + vhdPath);
throw new InternalErrorException("unable to get virtual size from vhd file");
}
return info;
}
@Override
public long getVirtualSize(File file) throws IOException {
try {
long size = getTemplateVirtualSize(file);
return size;
} catch (Exception e) {
s_logger.info("[ignored]" + "failed to get template virtual size for VHD: " + e.getLocalizedMessage());
}
return file.length();
}
protected long getTemplateVirtualSize(File file) throws IOException {
byte[] currentSize = new byte[8];
byte[] cookie = new byte[8];
byte[] creatorApp = new byte[4];
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 {
//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 - 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);
}
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
_name = name;
_storage = (StorageLayer)params.get(StorageLayer.InstanceConfigKey);
if (_storage == null) {
throw new ConfigurationException("Unable to get storage implementation");
}
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;
}
}