public bool VirtualToLogicalDriveOffset(ulong virtualOffset, out ulong logicalOffset) { logicalOffset = 0; if (virtualOffset >= Header.DriveSize) { throw new InvalidOperationException( $"Virtual offset 0x{virtualOffset:X} is outside drivedata length 0x{Header.DriveSize:X}"); } if (Header.Type > XvdType.Dynamic) { throw new NotSupportedException($"Xvd type {Header.Type} is unhandled"); } if (Header.Type == XvdType.Dynamic) { var dataStartOffset = virtualOffset + XvdMath.PageNumberToOffset(Header.NumberOfMetadataPages); var pageNumber = XvdMath.OffsetToPageNumber(dataStartOffset); var inBlockOffset = XvdMath.InBlockOffset(dataStartOffset); var firstDynamicPage = XvdMath.QueryFirstDynamicPage(Header.NumberOfMetadataPages); if (pageNumber >= firstDynamicPage) { var firstDynamicPageBytes = XvdMath.PageNumberToOffset(firstDynamicPage); var blockNumber = XvdMath.OffsetToBlockNumber(dataStartOffset - firstDynamicPageBytes); ulong allocatedBlock = ReadBat(blockNumber); if (allocatedBlock == INVALID_SECTOR) { return(false); } dataStartOffset = XvdMath.PageNumberToOffset(allocatedBlock) + inBlockOffset; pageNumber = XvdMath.OffsetToPageNumber(dataStartOffset); } var dataBackingBlockNum = XvdMath.ComputeDataBackingPageNumber(Header.Type, HashTreeLevels, HashTreePageCount, pageNumber); logicalOffset = XvdMath.PageNumberToOffset(dataBackingBlockNum); logicalOffset += XvdMath.InPageOffset(dataStartOffset); logicalOffset += XvdMath.PageNumberToOffset(Header.EmbeddedXvdPageCount); logicalOffset += Header.MutableDataLength; logicalOffset += XVD_HEADER_INCL_SIGNATURE_SIZE; logicalOffset += PAGE_SIZE; } else { // Xvd type fixed logicalOffset = virtualOffset; logicalOffset += XvdMath.PageNumberToOffset(Header.EmbeddedXvdPageCount); logicalOffset += Header.MutableDataLength; logicalOffset += XvdMath.PageNumberToOffset(Header.NumberOfMetadataPages); logicalOffset += XVD_HEADER_INCL_SIGNATURE_SIZE; logicalOffset += PAGE_SIZE; } return(true); }
byte[] ReadDynamic(int count) { int positionInBuffer = 0; int bytesRemaining = count; byte[] destBuffer = new byte[count]; while (positionInBuffer < count) { byte[] data = new byte[0]; if (Position < StaticDataLength) { // Read a chunk from non-dynamic area, next iteration will read dynamic data int maxReadLength = (int)(StaticDataLength - Position); int length = bytesRemaining > maxReadLength ? maxReadLength : bytesRemaining; data = InternalRead(length); } else { // Lookup block allocation table for real data offset var targetVirtualOffset = (ulong)(Position - StaticDataLength); ulong blockNumber = XvdMath.OffsetToBlockNumber(targetVirtualOffset); long inBlockOffset = (long)XvdMath.InBlockOffset(targetVirtualOffset); int maxReadLength = (int)(XvdFile.BLOCK_SIZE - inBlockOffset); int length = bytesRemaining > maxReadLength ? maxReadLength : bytesRemaining; var targetPage = _xvdFile.ReadBat(blockNumber); if (targetPage == XvdFile.INVALID_SECTOR) { data = new byte[length]; // Advance stream position cause we are not actually reading data Position += length; } else { long targetPhysicalOffset = DynamicBaseOffset + (long)XvdMath.PageNumberToOffset(targetPage) + inBlockOffset; data = InternalReadAbsolute(targetPhysicalOffset, length); } } Array.Copy(data, 0, destBuffer, positionInBuffer, data.Length); positionInBuffer += data.Length; bytesRemaining -= data.Length; } return(destBuffer); }