/// <summary> /// Initiates the build process. /// </summary> /// <param name="baseName">The base name for the VMDK, for example 'foo' to create 'foo.vhd'.</param> /// <returns>A set of one or more logical files that constitute the VHD. The first file is /// the 'primary' file that is normally attached to VMs.</returns> public override DiskImageFileSpecification[] Build(string baseName) { if (string.IsNullOrEmpty(baseName)) { throw new ArgumentException("Invalid base file name", "baseName"); } if (Content == null) { throw new InvalidOperationException("No content stream specified"); } List<DiskImageFileSpecification> fileSpecs = new List<DiskImageFileSpecification>(); Geometry geometry = Geometry ?? Geometry.FromCapacity(Content.Length); Footer footer = new Footer(geometry, Content.Length, DiskType); if (_diskType == FileType.Fixed) { footer.UpdateChecksum(); byte[] footerSector = new byte[Sizes.Sector]; footer.ToBytes(footerSector, 0); SparseStream footerStream = SparseStream.FromStream(new MemoryStream(footerSector, false), Ownership.None); Stream imageStream = new ConcatStream(Ownership.None, Content, footerStream); fileSpecs.Add(new DiskImageFileSpecification(baseName + ".vhd", new PassthroughStreamBuilder(imageStream))); } else if (_diskType == FileType.Dynamic) { fileSpecs.Add(new DiskImageFileSpecification(baseName + ".vhd", new DynamicDiskBuilder(Content, footer, (uint)Sizes.OneMiB * 2))); } else { throw new InvalidOperationException("Only Fixed and Dynamic disk types supported"); } return fileSpecs.ToArray(); }
/// <summary> /// Creates a new stream that contains the XVA image. /// </summary> /// <returns>The new stream.</returns> public override SparseStream Build() { TarFileBuilder tarBuilder = new TarFileBuilder(); int[] diskIds; string ovaFileContent = GenerateOvaXml(out diskIds); tarBuilder.AddFile("ova.xml", Encoding.ASCII.GetBytes(ovaFileContent)); int diskIdx = 0; foreach (var diskRec in _disks) { SparseStream diskStream = diskRec.Second; List<StreamExtent> extents = new List<StreamExtent>(diskStream.Extents); int lastChunkAdded = -1; foreach (StreamExtent extent in extents) { int firstChunk = (int)(extent.Start / Sizes.OneMiB); int lastChunk = (int)((extent.Start + extent.Length - 1) / Sizes.OneMiB); for (int i = firstChunk; i <= lastChunk; ++i) { if (i != lastChunkAdded) { HashAlgorithm hashAlg = new SHA1Managed(); Stream chunkStream; long diskBytesLeft = diskStream.Length - (i * Sizes.OneMiB); if (diskBytesLeft < Sizes.OneMiB) { chunkStream = new ConcatStream( Ownership.Dispose, new SubStream(diskStream, i * Sizes.OneMiB, diskBytesLeft), new ZeroStream(Sizes.OneMiB - diskBytesLeft)); } else { chunkStream = new SubStream(diskStream, i * Sizes.OneMiB, Sizes.OneMiB); } HashStream chunkHashStream = new HashStream(chunkStream, Ownership.Dispose, hashAlg); tarBuilder.AddFile(string.Format(CultureInfo.InvariantCulture, "Ref:{0}/{1:D8}", diskIds[diskIdx], i), chunkHashStream); tarBuilder.AddFile(string.Format(CultureInfo.InvariantCulture, "Ref:{0}/{1:D8}.checksum", diskIds[diskIdx], i), new ChecksumStream(hashAlg)); lastChunkAdded = i; } } } // Make sure the last chunk is present, filled with zero's if necessary int lastActualChunk = (int)((diskStream.Length - 1) / Sizes.OneMiB); if (lastChunkAdded < lastActualChunk) { HashAlgorithm hashAlg = new SHA1Managed(); Stream chunkStream = new ZeroStream(Sizes.OneMiB); HashStream chunkHashStream = new HashStream(chunkStream, Ownership.Dispose, hashAlg); tarBuilder.AddFile(string.Format(CultureInfo.InvariantCulture, "Ref:{0}/{1:D8}", diskIds[diskIdx], lastActualChunk), chunkHashStream); tarBuilder.AddFile(string.Format(CultureInfo.InvariantCulture, "Ref:{0}/{1:D8}.checksum", diskIds[diskIdx], lastActualChunk), new ChecksumStream(hashAlg)); } ++diskIdx; } return tarBuilder.Build(); }