Skip to content

Commit

Permalink
[VirtualPC disk image] Implement support for writing sparse (dynamica…
Browse files Browse the repository at this point in the history
…lly allocated) images. Fixes #645
  • Loading branch information
claunia committed Jan 2, 2024
1 parent 75d9d0e commit 64c4c88
Show file tree
Hide file tree
Showing 4 changed files with 274 additions and 21 deletions.
9 changes: 6 additions & 3 deletions Aaru.Images/VHD/Properties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,13 @@ public sealed partial class Vhd
MediaType.PCCardTypeIV
};

// TODO: Support dynamic images
/// <inheritdoc />
public IEnumerable<(string name, Type type, string description, object @default)> SupportedOptions =>
Array.Empty<(string name, Type type, string description, object @default)>();
public IEnumerable<(string name, Type type, string description, object @default)> SupportedOptions => new[]
{
("dynamic", typeof(bool), "Create a dynamic image, empty sectors are ignored", (object)true),
("block_size", typeof(uint),
"Block size. Must be a power of 2. Only 2MiB and 512KiB are supported by VirtualPC", 2097152)
};

/// <inheritdoc />
public IEnumerable<string> KnownExtensions => new[]
Expand Down
23 changes: 11 additions & 12 deletions Aaru.Images/VHD/Read.cs
Original file line number Diff line number Diff line change
Expand Up @@ -379,8 +379,7 @@ public ErrorNumber Open(IFilter imageFilter)

_thisDynamic = new DynamicDiskHeader
{
LocatorEntries = new ParentLocatorEntry[8],
Reserved2 = new byte[256]
LocatorEntries = new ParentLocatorEntry[8], Reserved2 = new byte[256]
};

for(var i = 0; i < 8; i++)
Expand Down Expand Up @@ -475,8 +474,6 @@ public ErrorNumber Open(IFilter imageFilter)
var batStopwatch = new Stopwatch();
batStopwatch.Start();

_blockAllocationTable = new uint[_thisDynamic.MaxTableEntries];

var bat = new byte[_thisDynamic.MaxTableEntries * 4];
imageStream.Seek((long)_thisDynamic.TableOffset, SeekOrigin.Begin);
imageStream.EnsureRead(bat, 0, bat.Length);
Expand All @@ -485,9 +482,6 @@ public ErrorNumber Open(IFilter imageFilter)

_blockAllocationTable = MemoryMarshal.Cast<byte, uint>(span)[..(int)_thisDynamic.MaxTableEntries].ToArray();

for(var i = 0; i < _blockAllocationTable.Length; i++)
_blockAllocationTable[i] = Swapping.Swap(_blockAllocationTable[i]);

batStopwatch.Stop();

AaruConsole.DebugWriteLine(MODULE_NAME, Localization.Filling_the_BAT_took_0_seconds,
Expand Down Expand Up @@ -704,7 +698,9 @@ public ErrorNumber ReadSector(ulong sectorAddress, out byte[] buffer)
// Sector number inside of block
var sectorInBlock = (uint)(sectorAddress % (_thisDynamic.BlockSize / 512));

if(_blockAllocationTable[blockNumber] == 0xFFFFFFFF)
uint blockPosition = Swapping.Swap(_blockAllocationTable[blockNumber]);

if(blockPosition == 0xFFFFFFFF)
{
buffer = new byte[512];

Expand All @@ -714,7 +710,7 @@ public ErrorNumber ReadSector(ulong sectorAddress, out byte[] buffer)
var bitmap = new byte[_bitmapSize * 512];

// Offset of block in file
long blockOffset = _blockAllocationTable[blockNumber] * 512L;
long blockOffset = blockPosition * 512L;

var bitmapByte = (int)Math.Floor((double)sectorInBlock / 8);
var bitmapBit = (int)(sectorInBlock % 8);
Expand Down Expand Up @@ -748,7 +744,7 @@ public ErrorNumber ReadSector(ulong sectorAddress, out byte[] buffer)
*/

buffer = new byte[512];
uint sectorOffset = _blockAllocationTable[blockNumber] + _bitmapSize + sectorInBlock;
uint sectorOffset = blockPosition + _bitmapSize + sectorInBlock;
thisStream = _thisFilter.GetDataForkStream();

thisStream.Seek(sectorOffset * 512, SeekOrigin.Begin);
Expand Down Expand Up @@ -821,14 +817,17 @@ public ErrorNumber ReadSectors(ulong sectorAddress, uint length, out byte[] buff
else
sectorsToReadHere = length;

// Keep the BAT in memory in big endian
uint blockPosition = Swapping.Swap(_blockAllocationTable[blockNumber]);

// Offset of sector in file
uint sectorOffset = _blockAllocationTable[blockNumber] + _bitmapSize + sectorInBlock;
uint sectorOffset = blockPosition + _bitmapSize + sectorInBlock;

// Data that can be read in this block
var prefix = new byte[sectorsToReadHere * 512];

// 0xFFFFFFFF means unallocated
if(_blockAllocationTable[blockNumber] != 0xFFFFFFFF)
if(blockPosition != 0xFFFFFFFF)
{
Stream thisStream = _thisFilter.GetDataForkStream();
thisStream.Seek(sectorOffset * 512, SeekOrigin.Begin);
Expand Down
7 changes: 7 additions & 0 deletions Aaru.Images/VHD/VHD.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ public sealed partial class Vhd : IWritableImage
const string MODULE_NAME = "Virtual PC plugin";
uint _bitmapSize;
uint[] _blockAllocationTable;
bool _blockInCache;
uint _blockSize;
byte[] _cachedBlock;
uint _cachedBlockNumber;
long _cachedBlockPosition;
long _currentFooterPosition;
bool _dynamic;
ImageInfo _imageInfo;
byte[][] _locatorEntriesData;
DateTime _parentDateTime;
Expand Down
Loading

0 comments on commit 64c4c88

Please sign in to comment.