public HostedSparseExtentStream(Stream file, Ownership ownsFile, long diskOffset, SparseStream parentDiskStream, Ownership ownsParentDiskStream) { _fileStream = file; _ownsFileStream = ownsFile; _diskOffset = diskOffset; _parentDiskStream = parentDiskStream; _ownsParentDiskStream = ownsParentDiskStream; file.Position = 0; byte[] headerSector = Utilities.ReadFully(file, Sizes.Sector); _hostedHeader = HostedSparseExtentHeader.Read(headerSector, 0); if (_hostedHeader.GdOffset == -1) { // Fall back to secondary copy that (should) be at the end of the stream, just before the end-of-stream sector marker file.Position = file.Length - Sizes.OneKiB; headerSector = Utilities.ReadFully(file, Sizes.Sector); _hostedHeader = HostedSparseExtentHeader.Read(headerSector, 0); if (_hostedHeader.MagicNumber != HostedSparseExtentHeader.VmdkMagicNumber) { throw new IOException("Unable to locate valid VMDK header or footer"); } } _header = _hostedHeader; if (_hostedHeader.CompressAlgorithm != 0 && _hostedHeader.CompressAlgorithm != 1) { throw new NotSupportedException("Only uncompressed and DEFLATE compressed disks supported"); } _gtCoverage = _header.NumGTEsPerGT * _header.GrainSize * Sizes.Sector; LoadGlobalDirectory(); }
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); } } } }
private static void CreateSparseExtent(Stream extentStream, long size, long descriptorLength, out long descriptorStart) { // Figure out grain size and number of grain tables, and adjust actual extent size to be a multiple // of grain size const int GtesPerGt = 512; long grainSize = 128; int numGrainTables = (int)Utilities.Ceil(size, grainSize * GtesPerGt * Sizes.Sector); descriptorLength = Utilities.RoundUp(descriptorLength, Sizes.Sector); descriptorStart = 0; if (descriptorLength != 0) { descriptorStart = 1; } long redundantGrainDirStart = Math.Max(descriptorStart, 1) + Utilities.Ceil(descriptorLength, Sizes.Sector); long redundantGrainDirLength = numGrainTables * 4; long redundantGrainTablesStart = redundantGrainDirStart + Utilities.Ceil(redundantGrainDirLength, Sizes.Sector); long redundantGrainTablesLength = numGrainTables * Utilities.RoundUp(GtesPerGt * 4, Sizes.Sector); long grainDirStart = redundantGrainTablesStart + Utilities.Ceil(redundantGrainTablesLength, Sizes.Sector); long grainDirLength = numGrainTables * 4; long grainTablesStart = grainDirStart + Utilities.Ceil(grainDirLength, Sizes.Sector); long grainTablesLength = numGrainTables * Utilities.RoundUp(GtesPerGt * 4, Sizes.Sector); long dataStart = Utilities.RoundUp(grainTablesStart + Utilities.Ceil(grainTablesLength, Sizes.Sector), grainSize); // Generate the header, and write it HostedSparseExtentHeader header = new HostedSparseExtentHeader(); header.Flags = HostedSparseExtentFlags.ValidLineDetectionTest | HostedSparseExtentFlags.RedundantGrainTable; header.Capacity = Utilities.RoundUp(size, grainSize * Sizes.Sector) / Sizes.Sector; header.GrainSize = grainSize; header.DescriptorOffset = descriptorStart; header.DescriptorSize = descriptorLength / Sizes.Sector; header.NumGTEsPerGT = GtesPerGt; header.RgdOffset = redundantGrainDirStart; header.GdOffset = grainDirStart; header.Overhead = dataStart; extentStream.Position = 0; extentStream.Write(header.GetBytes(), 0, Sizes.Sector); // Zero-out the descriptor space if (descriptorLength > 0) { byte[] descriptor = new byte[descriptorLength]; extentStream.Position = descriptorStart * Sizes.Sector; extentStream.Write(descriptor, 0, descriptor.Length); } // Generate the redundant grain dir, and write it byte[] grainDir = new byte[numGrainTables * 4]; for (int i = 0; i < numGrainTables; ++i) { Utilities.WriteBytesLittleEndian((uint)(redundantGrainTablesStart + (i * Utilities.Ceil(GtesPerGt * 4, Sizes.Sector))), grainDir, i * 4); } extentStream.Position = redundantGrainDirStart * Sizes.Sector; extentStream.Write(grainDir, 0, grainDir.Length); // Write out the blank grain tables byte[] grainTable = new byte[GtesPerGt * 4]; for (int i = 0; i < numGrainTables; ++i) { extentStream.Position = (redundantGrainTablesStart * Sizes.Sector) + (i * Utilities.RoundUp(GtesPerGt * 4, Sizes.Sector)); extentStream.Write(grainTable, 0, grainTable.Length); } // Generate the main grain dir, and write it for (int i = 0; i < numGrainTables; ++i) { Utilities.WriteBytesLittleEndian((uint)(grainTablesStart + (i * Utilities.Ceil(GtesPerGt * 4, Sizes.Sector))), grainDir, i * 4); } extentStream.Position = grainDirStart * Sizes.Sector; extentStream.Write(grainDir, 0, grainDir.Length); // Write out the blank grain tables for (int i = 0; i < numGrainTables; ++i) { extentStream.Position = (grainTablesStart * Sizes.Sector) + (i * Utilities.RoundUp(GtesPerGt * 4, Sizes.Sector)); extentStream.Write(grainTable, 0, grainTable.Length); } // Make sure stream is correct length if (extentStream.Length != dataStart * Sizes.Sector) { extentStream.SetLength(dataStart * Sizes.Sector); } }
internal override List<BuilderExtent> FixExtents(out long totalLength) { List<BuilderExtent> extents = new List<BuilderExtent>(); MemoryStream descriptorStream = new MemoryStream(); _descriptor.Write(descriptorStream); // Figure out grain size and number of grain tables, and adjust actual extent size to be a multiple // of grain size const int GtesPerGt = 512; long grainSize = 128; int numGrainTables = (int)Utilities.Ceil(_content.Length, grainSize * GtesPerGt * Sizes.Sector); long descriptorLength = 10 * Sizes.OneKiB; // Utilities.RoundUp(descriptorStream.Length, Sizes.Sector); long descriptorStart = 0; if (descriptorLength != 0) { descriptorStart = 1; } long redundantGrainDirStart = Math.Max(descriptorStart, 1) + Utilities.Ceil(descriptorLength, Sizes.Sector); long redundantGrainDirLength = numGrainTables * 4; long redundantGrainTablesStart = redundantGrainDirStart + Utilities.Ceil(redundantGrainDirLength, Sizes.Sector); long redundantGrainTablesLength = numGrainTables * Utilities.RoundUp(GtesPerGt * 4, Sizes.Sector); long grainDirStart = redundantGrainTablesStart + Utilities.Ceil(redundantGrainTablesLength, Sizes.Sector); long grainDirLength = numGrainTables * 4; long grainTablesStart = grainDirStart + Utilities.Ceil(grainDirLength, Sizes.Sector); long grainTablesLength = numGrainTables * Utilities.RoundUp(GtesPerGt * 4, Sizes.Sector); long dataStart = Utilities.RoundUp(grainTablesStart + Utilities.Ceil(grainTablesLength, Sizes.Sector), grainSize); // Generate the header, and write it HostedSparseExtentHeader header = new HostedSparseExtentHeader(); header.Flags = HostedSparseExtentFlags.ValidLineDetectionTest | HostedSparseExtentFlags.RedundantGrainTable; header.Capacity = Utilities.RoundUp(_content.Length, grainSize * Sizes.Sector) / Sizes.Sector; header.GrainSize = grainSize; header.DescriptorOffset = descriptorStart; header.DescriptorSize = descriptorLength / Sizes.Sector; header.NumGTEsPerGT = GtesPerGt; header.RgdOffset = redundantGrainDirStart; header.GdOffset = grainDirStart; header.Overhead = dataStart; extents.Add(new BuilderBytesExtent(0, header.GetBytes())); // The descriptor extent if (descriptorLength > 0) { extents.Add(new BuilderStreamExtent(descriptorStart * Sizes.Sector, descriptorStream)); } // The grain directory extents extents.Add(new GrainDirectoryExtent(redundantGrainDirStart * Sizes.Sector, redundantGrainTablesStart, numGrainTables, GtesPerGt)); extents.Add(new GrainDirectoryExtent(grainDirStart * Sizes.Sector, grainTablesStart, numGrainTables, GtesPerGt)); // For each graintable span that's present... long dataSectorsUsed = 0; long gtSpan = GtesPerGt * grainSize * Sizes.Sector; foreach (var gtRange in StreamExtent.Blocks(_content.Extents, grainSize * GtesPerGt * Sizes.Sector)) { for (long i = 0; i < gtRange.Count; ++i) { int gt = (int)(gtRange.Offset + i); SubStream gtStream = new SubStream(_content, gt * gtSpan, Math.Min(gtSpan, _content.Length - (gt * gtSpan))); GrainTableDataExtent dataExtent = new GrainTableDataExtent((dataStart + dataSectorsUsed) * Sizes.Sector, gtStream, grainSize); extents.Add(dataExtent); extents.Add(new GrainTableExtent(GrainTablePosition(redundantGrainTablesStart, gt, GtesPerGt), gtStream, dataStart + dataSectorsUsed, GtesPerGt, grainSize)); extents.Add(new GrainTableExtent(GrainTablePosition(grainTablesStart, gt, GtesPerGt), gtStream, dataStart + dataSectorsUsed, GtesPerGt, grainSize)); dataSectorsUsed += dataExtent.Length / Sizes.Sector; } } totalLength = (dataStart + dataSectorsUsed) * Sizes.Sector; return extents; }
internal override List <BuilderExtent> FixExtents(out long totalLength) { List <BuilderExtent> extents = new List <BuilderExtent>(); MemoryStream descriptorStream = new MemoryStream(); _descriptor.Write(descriptorStream); // Figure out grain size and number of grain tables, and adjust actual extent size to be a multiple // of grain size const int GtesPerGt = 512; long grainSize = 128; int numGrainTables = (int)Utilities.Ceil(_content.Length, grainSize * GtesPerGt * Sizes.Sector); long descriptorLength = 10 * Sizes.OneKiB; // Utilities.RoundUp(descriptorStream.Length, Sizes.Sector); long descriptorStart = 0; if (descriptorLength != 0) { descriptorStart = 1; } long redundantGrainDirStart = Math.Max(descriptorStart, 1) + Utilities.Ceil(descriptorLength, Sizes.Sector); long redundantGrainDirLength = numGrainTables * 4; long redundantGrainTablesStart = redundantGrainDirStart + Utilities.Ceil(redundantGrainDirLength, Sizes.Sector); long redundantGrainTablesLength = numGrainTables * Utilities.RoundUp(GtesPerGt * 4, Sizes.Sector); long grainDirStart = redundantGrainTablesStart + Utilities.Ceil(redundantGrainTablesLength, Sizes.Sector); long grainDirLength = numGrainTables * 4; long grainTablesStart = grainDirStart + Utilities.Ceil(grainDirLength, Sizes.Sector); long grainTablesLength = numGrainTables * Utilities.RoundUp(GtesPerGt * 4, Sizes.Sector); long dataStart = Utilities.RoundUp(grainTablesStart + Utilities.Ceil(grainTablesLength, Sizes.Sector), grainSize); // Generate the header, and write it HostedSparseExtentHeader header = new HostedSparseExtentHeader(); header.Flags = HostedSparseExtentFlags.ValidLineDetectionTest | HostedSparseExtentFlags.RedundantGrainTable; header.Capacity = Utilities.RoundUp(_content.Length, grainSize * Sizes.Sector) / Sizes.Sector; header.GrainSize = grainSize; header.DescriptorOffset = descriptorStart; header.DescriptorSize = descriptorLength / Sizes.Sector; header.NumGTEsPerGT = GtesPerGt; header.RgdOffset = redundantGrainDirStart; header.GdOffset = grainDirStart; header.Overhead = dataStart; extents.Add(new BuilderBytesExtent(0, header.GetBytes())); // The descriptor extent if (descriptorLength > 0) { extents.Add(new BuilderStreamExtent(descriptorStart * Sizes.Sector, descriptorStream)); } // The grain directory extents extents.Add(new GrainDirectoryExtent(redundantGrainDirStart * Sizes.Sector, redundantGrainTablesStart, numGrainTables, GtesPerGt)); extents.Add(new GrainDirectoryExtent(grainDirStart * Sizes.Sector, grainTablesStart, numGrainTables, GtesPerGt)); // For each graintable span that's present... long dataSectorsUsed = 0; long gtSpan = GtesPerGt * grainSize * Sizes.Sector; foreach (var gtRange in StreamExtent.Blocks(_content.Extents, grainSize * GtesPerGt * Sizes.Sector)) { for (long i = 0; i < gtRange.Count; ++i) { int gt = (int)(gtRange.Offset + i); SubStream gtStream = new SubStream(_content, gt * gtSpan, Math.Min(gtSpan, _content.Length - (gt * gtSpan))); GrainTableDataExtent dataExtent = new GrainTableDataExtent((dataStart + dataSectorsUsed) * Sizes.Sector, gtStream, grainSize); extents.Add(dataExtent); extents.Add(new GrainTableExtent(GrainTablePosition(redundantGrainTablesStart, gt, GtesPerGt), gtStream, dataStart + dataSectorsUsed, GtesPerGt, grainSize)); extents.Add(new GrainTableExtent(GrainTablePosition(grainTablesStart, gt, GtesPerGt), gtStream, dataStart + dataSectorsUsed, GtesPerGt, grainSize)); dataSectorsUsed += dataExtent.Length / Sizes.Sector; } } totalLength = (dataStart + dataSectorsUsed) * Sizes.Sector; return(extents); }