public Footer(Footer toCopy) { Cookie = toCopy.Cookie; Features = toCopy.Features; FileFormatVersion = toCopy.FileFormatVersion; DataOffset = toCopy.DataOffset; Timestamp = toCopy.Timestamp; CreatorApp = toCopy.CreatorApp; CreatorVersion = toCopy.CreatorVersion; CreatorHostOS = toCopy.CreatorHostOS; OriginalSize = toCopy.OriginalSize; CurrentSize = toCopy.CurrentSize; Geometry = toCopy.Geometry; DiskType = toCopy.DiskType; Checksum = toCopy.Checksum; UniqueId = toCopy.UniqueId; SavedState = toCopy.SavedState; }
/// <summary> /// Initiates the build process. /// </summary> /// <param name="baseName">The base name for the VHD, 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(); }
public DynamicDiskBuilder(SparseStream content, Footer footer, uint blockSize) { _content = content; _footer = footer; _blockSize = blockSize; }
internal DiskImageFileInfo(Footer footer, DynamicHeader header, Stream vhdStream) { _footer = footer; _header = header; _vhdStream = vhdStream; }
private void CheckHeader() { _fileStream.Position = 0; byte[] headerSector = Utilities.ReadFully(_fileStream, Utilities.SectorSize); Footer header = Footer.FromBytes(headerSector, 0); if (!header.IsValid()) { ReportError("Invalid VHD footer at start of file"); } _fileStream.Position = _fileStream.Length - Utilities.SectorSize; byte[] footerSector = Utilities.ReadFully(_fileStream, Utilities.SectorSize); if (!Utilities.AreEqual(footerSector, headerSector)) { ReportError("Header and footer are different"); } if (_footer == null || !_footer.IsValid()) { _footer = header; } }
private void CheckFooter() { _fileStream.Position = _fileStream.Length - Utilities.SectorSize; byte[] sector = Utilities.ReadFully(_fileStream, Utilities.SectorSize); _footer = Footer.FromBytes(sector, 0); if (!_footer.IsValid()) { ReportError("Invalid VHD footer at end of file"); } }
private void ReadFooter(bool fallbackToFront) { _fileStream.Position = _fileStream.Length - Utilities.SectorSize; byte[] sector = Utilities.ReadFully(_fileStream, Utilities.SectorSize); _footer = Footer.FromBytes(sector, 0); if (!_footer.IsValid()) { if (!fallbackToFront) { throw new IOException("Corrupt VHD file - invalid footer at end (did not check front of file)"); } _fileStream.Position = 0; Utilities.ReadFully(_fileStream, sector, 0, Utilities.SectorSize); _footer = Footer.FromBytes(sector, 0); if (!_footer.IsValid()) { throw new IOException("Failed to find a valid VHD footer at start or end of file - VHD file is corrupt"); } } }
private static void InitializeFixedInternal(Stream stream, long capacity, Geometry geometry) { if (geometry == null) { geometry = Geometry.FromCapacity(capacity); } Footer footer = new Footer(geometry, capacity, FileType.Fixed); footer.UpdateChecksum(); byte[] sector = new byte[Utilities.SectorSize]; footer.ToBytes(sector, 0); stream.Position = Utilities.RoundUp(capacity, Utilities.SectorSize); stream.Write(sector, 0, sector.Length); stream.SetLength(stream.Position); stream.Position = 0; }
private static void InitializeDynamicInternal(Stream stream, long capacity, Geometry geometry, long blockSize) { if (blockSize > uint.MaxValue || blockSize < 0) { throw new ArgumentOutOfRangeException("blockSize", "Must be in the range 0 to uint.MaxValue"); } if (geometry == null) { geometry = Geometry.FromCapacity(capacity); } Footer footer = new Footer(geometry, capacity, FileType.Dynamic); footer.DataOffset = 512; // Offset of Dynamic Header footer.UpdateChecksum(); byte[] footerBlock = new byte[512]; footer.ToBytes(footerBlock, 0); DynamicHeader dynamicHeader = new DynamicHeader(-1, 1024 + 512, (uint)blockSize, capacity); dynamicHeader.UpdateChecksum(); byte[] dynamicHeaderBlock = new byte[1024]; dynamicHeader.ToBytes(dynamicHeaderBlock, 0); int batSize = (((dynamicHeader.MaxTableEntries * 4) + Utilities.SectorSize - 1) / Utilities.SectorSize) * Utilities.SectorSize; byte[] bat = new byte[batSize]; for (int i = 0; i < bat.Length; ++i) { bat[i] = 0xFF; } stream.Position = 0; stream.Write(footerBlock, 0, 512); stream.Write(dynamicHeaderBlock, 0, 1024); stream.Write(bat, 0, batSize); stream.Write(footerBlock, 0, 512); }
private static void InitializeDifferencingInternal(Stream stream, DiskImageFile parent, string parentAbsolutePath, string parentRelativePath, DateTime parentModificationTimeUtc) { Footer footer = new Footer(parent.Geometry, parent._footer.CurrentSize, FileType.Differencing); footer.DataOffset = 512; // Offset of Dynamic Header footer.OriginalSize = parent._footer.OriginalSize; footer.UpdateChecksum(); byte[] footerBlock = new byte[512]; footer.ToBytes(footerBlock, 0); long tableOffset = 512 + 1024; // Footer + Header uint blockSize = (parent._dynamicHeader == null) ? DynamicHeader.DefaultBlockSize : parent._dynamicHeader.BlockSize; DynamicHeader dynamicHeader = new DynamicHeader(-1, tableOffset, blockSize, footer.CurrentSize); int batSize = (((dynamicHeader.MaxTableEntries * 4) + Utilities.SectorSize - 1) / Utilities.SectorSize) * Utilities.SectorSize; dynamicHeader.ParentUniqueId = parent.UniqueId; dynamicHeader.ParentTimestamp = parentModificationTimeUtc; dynamicHeader.ParentUnicodeName = Utilities.GetFileFromPath(parentAbsolutePath); dynamicHeader.ParentLocators[7].PlatformCode = ParentLocator.PlatformCodeWindowsAbsoluteUnicode; dynamicHeader.ParentLocators[7].PlatformDataSpace = 512; dynamicHeader.ParentLocators[7].PlatformDataLength = parentAbsolutePath.Length * 2; dynamicHeader.ParentLocators[7].PlatformDataOffset = tableOffset + batSize; dynamicHeader.ParentLocators[6].PlatformCode = ParentLocator.PlatformCodeWindowsRelativeUnicode; dynamicHeader.ParentLocators[6].PlatformDataSpace = 512; dynamicHeader.ParentLocators[6].PlatformDataLength = parentRelativePath.Length * 2; dynamicHeader.ParentLocators[6].PlatformDataOffset = tableOffset + batSize + 512; dynamicHeader.UpdateChecksum(); byte[] dynamicHeaderBlock = new byte[1024]; dynamicHeader.ToBytes(dynamicHeaderBlock, 0); byte[] platformLocator1 = new byte[512]; Encoding.Unicode.GetBytes(parentAbsolutePath, 0, parentAbsolutePath.Length, platformLocator1, 0); byte[] platformLocator2 = new byte[512]; Encoding.Unicode.GetBytes(parentRelativePath, 0, parentRelativePath.Length, platformLocator2, 0); byte[] bat = new byte[batSize]; for (int i = 0; i < bat.Length; ++i) { bat[i] = 0xFF; } stream.Position = 0; stream.Write(footerBlock, 0, 512); stream.Write(dynamicHeaderBlock, 0, 1024); stream.Write(bat, 0, batSize); stream.Write(platformLocator1, 0, 512); stream.Write(platformLocator2, 0, 512); stream.Write(footerBlock, 0, 512); }
private uint CalculateChecksum() { Footer copy = new Footer(this); copy.Checksum = 0; byte[] asBytes = new byte[512]; copy.ToBytes(asBytes, 0); uint checksum = 0; foreach (uint value in asBytes) { checksum += value; } checksum = ~checksum; return checksum; }
public static Footer FromBytes(byte[] buffer, int offset) { Footer result = new Footer(); result.Cookie = Utilities.BytesToString(buffer, offset + 0, 8); result.Features = Utilities.ToUInt32BigEndian(buffer, offset + 8); result.FileFormatVersion = Utilities.ToUInt32BigEndian(buffer, offset + 12); result.DataOffset = Utilities.ToInt64BigEndian(buffer, offset + 16); result.Timestamp = EpochUtc.AddSeconds(Utilities.ToUInt32BigEndian(buffer, offset + 24)); result.CreatorApp = Utilities.BytesToString(buffer, offset + 28, 4); result.CreatorVersion = Utilities.ToUInt32BigEndian(buffer, offset + 32); result.CreatorHostOS = Utilities.BytesToString(buffer, offset + 36, 4); result.OriginalSize = Utilities.ToInt64BigEndian(buffer, offset + 40); result.CurrentSize = Utilities.ToInt64BigEndian(buffer, offset + 48); result.Geometry = new Geometry(Utilities.ToUInt16BigEndian(buffer, offset + 56), buffer[58], buffer[59]); result.DiskType = (FileType)Utilities.ToUInt32BigEndian(buffer, offset + 60); result.Checksum = Utilities.ToUInt32BigEndian(buffer, offset + 64); result.UniqueId = Utilities.ToGuidBigEndian(buffer, offset + 68); result.SavedState = buffer[84]; return result; }
private static void InitializeDynamicInternal(Stream stream, long capacity, Geometry geometry, long blockSize) { if (blockSize > uint.MaxValue || blockSize < 0) { throw new ArgumentOutOfRangeException("blockSize", "Must be in the range 0 to uint.MaxValue"); } if (geometry == null) { geometry = Geometry.FromCapacity(capacity); // Backwards compatibility - if the geometry isn't specified, make the actual capacity a // multiple of the disk geometry's cylinder capacity. long cylinderSize = geometry.BytesPerSector * (long)geometry.SectorsPerTrack * geometry.HeadsPerCylinder; capacity = (capacity / cylinderSize) * cylinderSize; } Footer footer = new Footer(geometry, capacity, FileType.Dynamic); footer.DataOffset = 512; // Offset of Dynamic Header footer.UpdateChecksum(); byte[] footerBlock = new byte[512]; footer.ToBytes(footerBlock, 0); DynamicHeader dynamicHeader = new DynamicHeader(-1, 1024 + 512, (uint)blockSize, capacity); dynamicHeader.UpdateChecksum(); byte[] dynamicHeaderBlock = new byte[1024]; dynamicHeader.ToBytes(dynamicHeaderBlock, 0); int batSize = (((dynamicHeader.MaxTableEntries * 4) + Utilities.SectorSize - 1) / Utilities.SectorSize) * Utilities.SectorSize; byte[] bat = new byte[batSize]; for (int i = 0; i < bat.Length; ++i) { bat[i] = 0xFF; } stream.Position = 0; stream.Write(footerBlock, 0, 512); stream.Write(dynamicHeaderBlock, 0, 1024); stream.Write(bat, 0, batSize); stream.Write(footerBlock, 0, 512); }
private static void InitializeFixedInternal(Stream stream, long capacity, Geometry geometry) { if (geometry == null) { geometry = Geometry.FromCapacity(capacity); // This is to maintain legacy behaviour - if no geometry specified, we make the actual capacity exactly // fit the disk geometry, rather than allowing it to be different. capacity = geometry.Capacity; } Footer footer = new Footer(geometry, capacity, FileType.Fixed); footer.UpdateChecksum(); byte[] sector = new byte[Utilities.SectorSize]; footer.ToBytes(sector, 0); stream.Position = Utilities.RoundUp(capacity, Utilities.SectorSize); stream.Write(sector, 0, sector.Length); stream.SetLength(stream.Position); stream.Position = 0; }