private void LoadDescriptor(Stream s) { s.Position = 0; byte[] header = Utilities.ReadFully(s, (int)Math.Min(Sizes.Sector, s.Length)); if (header.Length < Sizes.Sector || Utilities.ToUInt32LittleEndian(header, 0) != HostedSparseExtentHeader.VmdkMagicNumber) { s.Position = 0; _descriptor = new DescriptorFile(s); if (_access != FileAccess.Read) { _descriptor.ContentId = (uint)_rng.Next(); s.Position = 0; _descriptor.Write(s); s.SetLength(s.Position); } } else { // This is a sparse disk extent, hopefully with embedded descriptor... HostedSparseExtentHeader hdr = HostedSparseExtentHeader.Read(header, 0); if (hdr.DescriptorOffset != 0) { Stream descriptorStream = new SubStream(s, hdr.DescriptorOffset * Sizes.Sector, hdr.DescriptorSize * Sizes.Sector); _descriptor = new DescriptorFile(descriptorStream); if (_access != FileAccess.Read) { _descriptor.ContentId = (uint)_rng.Next(); descriptorStream.Position = 0; _descriptor.Write(descriptorStream); byte[] blank = new byte[descriptorStream.Length - descriptorStream.Position]; descriptorStream.Write(blank, 0, blank.Length); } } } }
/// <summary> /// Write IVFC hash levels. /// </summary> /// <param name="output">The stream to write to.</param> /// <param name="metaDataPosition">The position of the initial data to hash.</param> /// <param name="metaDataSize">The position at which to start writing.</param> /// <param name="masterHashPosition">The separate position at which the master hash level is written.</param> /// <param name="levels">Number of levels to write.</param> /// <returns>Position and size of each written level.</returns> private static IList <(long, long, long)> WriteIvfcLevels(Stream output, long metaDataPosition, long metaDataSize, long masterHashPosition, int levels) { // Pre-calculate hash level sizes var hashLevelSizes = new long[levels]; var alignedMetaDataSize = (metaDataSize + BlockSize_ - 1) & ~(BlockSize_ - 1); for (var level = 0; level < levels - 1; level++) { var previousSize = level == 0 ? alignedMetaDataSize : hashLevelSizes[level - 1]; var levelSize = previousSize / BlockSize_ * 0x20; var alignedLevelSize = (levelSize + BlockSize_ - 1) & ~(BlockSize_ - 1); hashLevelSizes[level] = alignedLevelSize; } // Pre-calculate hash level position var hashLevelPositions = new long[levels]; var alignedMetaDataPosition = (metaDataPosition + BlockSize_ - 1) & ~(BlockSize_ - 1); for (var level = 0; level < levels - 1; level++) { var levelPosition = alignedMetaDataPosition + alignedMetaDataSize + hashLevelSizes.Skip(level + 1).Take(levels - level - 2).Sum(x => x); hashLevelPositions[level] = levelPosition; } // Add master hash position hashLevelSizes[levels - 1] = BlockSize_; hashLevelPositions[levels - 1] = masterHashPosition; // Write hash levels var result = new List <(long, long, long)>(); var sha256 = new Kryptography.Hash.Sha256(); var previousLevelPosition = alignedMetaDataPosition; var previousLevelSize = alignedMetaDataSize; for (var level = 0; level < levels; level++) { var previousLevelStream = new SubStream(output, previousLevelPosition, previousLevelSize); var levelStream = new SubStream(output, hashLevelPositions[level], hashLevelSizes[level]); var block = new byte[BlockSize_]; while (previousLevelStream.Position < previousLevelStream.Length) { previousLevelStream.Read(block, 0, BlockSize_); var hash = sha256.Compute(block); levelStream.Write(hash); } result.Add((hashLevelPositions[level], levelStream.Position, hashLevelSizes[level])); previousLevelPosition = hashLevelPositions[level]; previousLevelSize = hashLevelSizes[level]; } //var dataPosition = metaDataPosition; //var writePosition = hashLevelInformation[0]; //var dataSize = writePosition - dataPosition; //for (var level = 0; level < levels; level++) //{ // bw.BaseStream.Position = writePosition; // var dataEnd = dataPosition + dataSize; // while (dataPosition < dataEnd) // { // var blockSize = Math.Min(BlockSize_, dataEnd - dataPosition); // var hash = sha256.Compute(new SubStream(output, dataPosition, blockSize)); // bw.Write(hash); // dataPosition += BlockSize_; // } // dataPosition = writePosition; // dataSize = bw.BaseStream.Position - writePosition; // writePosition = level + 1 >= levels - 1 ? masterHashPosition : hashLevelInformation[level + 1]; // // Pad hash level to next block // // Do not pad master hash level // // TODO: Make general padding code that also works with unaligned master hash position // var alignSize = 0L; // if (level + 1 < levels - 1) // { // alignSize = ((dataSize + BlockSize_ - 1) & ~(BlockSize_ - 1)) - dataSize; // bw.WritePadding((int)alignSize); // } // result.Add((dataPosition, dataSize, dataSize + alignSize)); //} return(result); }
public void Write() { // Verify that write fails for empty parent streams. using (var parent = new MemoryStream()) { using (var substream = new SubStream(parent, 0, 0)) { Assert.Throws <IOException>(() => substream.Write(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 })); Assert.Equal(0, substream.Position); } } // Verify that we can write to a substream that spans the entire parent. using (var parent = new MemoryStream()) { parent.Write(new byte[10]); using (var substream = new SubStream(parent, 0, 10)) { substream.Write(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); Assert.Equal(10, substream.Position); } } // Verify that we can write to a substream that spans the only part parent. using (var parent = new MemoryStream()) { parent.Write(new byte[10]); using (var substream = new SubStream(parent, 2, 5)) { var buffer = new byte[5]; substream.Write(new byte[] { 1, 2, 3, 4, 5 }); Assert.Equal(5, substream.Position); buffer = new byte[10]; parent.Position = 0; parent.Read(buffer, 0, 10); Assert.Equal(new byte[] { 0, 0, 1, 2, 3, 4, 5, 0, 0, 0 }, buffer); } } // Verify that we can't write past the end of the substream data // when the substream spans the entire parent. using (var parent = new MemoryStream()) { parent.Write(new byte[10]); using (var substream = new SubStream(parent, 0, 10)) { var buffer = new byte[11]; Assert.Throws <IOException>(() => substream.Write(buffer)); } } // Verify that we can't write past the end of the substream data // when the substream is fully contained within the parent. using (var parent = new MemoryStream()) { parent.Write(new byte[20]); using (var substream = new SubStream(parent, 5, 10)) { var buffer = new byte[11]; Assert.Throws <IOException>(() => substream.Write(buffer)); } } }