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