Skip to content

Commit

Permalink
Merge pull request #5 from glenebob/vhdx-logentry-tryread-improvements
Browse files Browse the repository at this point in the history
VHDX LogEntry.TryRead() Bug Fix and Improvements
  • Loading branch information
LTRData authored Mar 1, 2024
2 parents 8699b63 + b0ecacc commit 59e6430
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 77 deletions.
116 changes: 55 additions & 61 deletions Library/DiscUtils.Vhdx/LogEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ namespace DiscUtils.Vhdx;

internal sealed class LogEntry
{
public const int LogSectorSize = (int)(4 * Sizes.OneKiB);
private readonly List<Descriptor> _descriptors = new List<Descriptor>();
public const int LogSectorSize = 4 * Sizes.OneKiB;

private readonly List<Descriptor> _descriptors = new List<Descriptor>();
private readonly LogEntryHeader _header;

private LogEntry(long position, LogEntryHeader header, List<Descriptor> descriptors)
Expand Down Expand Up @@ -101,90 +101,84 @@ public static bool TryRead(Stream logStream, out LogEntry entry)
{
var position = logStream.Position;

var sectorBuffer = ArrayPool<byte>.Shared.Rent(LogSectorSize);
var bytesToRead = LogEntryHeader.ByteCount;
Span<byte> headerBuffer = stackalloc byte[bytesToRead];
if (logStream.ReadMaximum(headerBuffer) != bytesToRead)
{
entry = null;
return false;
}

var header = new LogEntryHeader();
header.ReadFrom(headerBuffer);

if (!header.IsValid)
{
entry = null;
return false;
}

var entryLength = checked((int)header.EntryLength);
var logEntryBuffer = ArrayPool<byte>.Shared.Rent(entryLength);

try
{
if (logStream.ReadMaximum(sectorBuffer, 0, LogSectorSize) != LogSectorSize)
{
entry = null;
return false;
}
headerBuffer.CopyTo(logEntryBuffer);

var sig = EndianUtilities.ToUInt32LittleEndian(sectorBuffer, 0);
if (sig != LogEntryHeader.LogEntrySignature)
bytesToRead = entryLength - LogEntryHeader.ByteCount;
if (logStream.ReadMaximum(logEntryBuffer, LogEntryHeader.ByteCount, bytesToRead) != bytesToRead)
{
entry = null;
return false;
}

var header = new LogEntryHeader();
header.ReadFrom(sectorBuffer.AsSpan(0, LogSectorSize));

if (!header.IsValid || header.EntryLength > logStream.Length)
Array.Clear(logEntryBuffer, 4, sizeof(uint));
if (header.Checksum !=
Crc32LittleEndian.Compute(Crc32Algorithm.Castagnoli, logEntryBuffer, 0, entryLength))
{
entry = null;
return false;
}

var logEntryBuffer = ArrayPool<byte>.Shared.Rent(checked((int)header.EntryLength));
try
{
System.Buffer.BlockCopy(sectorBuffer, 0, logEntryBuffer, 0, LogSectorSize);

logStream.ReadExactly(logEntryBuffer, LogSectorSize, checked((int)(header.EntryLength - LogSectorSize)));

EndianUtilities.WriteBytesLittleEndian(0, logEntryBuffer, 4);
if (header.Checksum !=
Crc32LittleEndian.Compute(Crc32Algorithm.Castagnoli, logEntryBuffer, 0, (int)header.EntryLength))
{
entry = null;
return false;
}
var dataPos = MathUtilities.RoundUp((int)header.DescriptorCount * 32 + 64, LogSectorSize);

var dataPos = MathUtilities.RoundUp((int)header.DescriptorCount * 32 + 64, LogSectorSize);
var descriptors = new List<Descriptor>();
for (var i = 0; i < header.DescriptorCount; ++i)
{
var offset = i * 32 + 64;
Descriptor descriptor;

var descriptors = new List<Descriptor>();
for (var i = 0; i < header.DescriptorCount; ++i)
var descriptorSig = EndianUtilities.ToUInt32LittleEndian(logEntryBuffer, offset);
switch (descriptorSig)
{
var offset = i * 32 + 64;
Descriptor descriptor;

var descriptorSig = EndianUtilities.ToUInt32LittleEndian(logEntryBuffer, offset);
switch (descriptorSig)
{
case Descriptor.ZeroDescriptorSignature:
descriptor = new ZeroDescriptor();
break;
case Descriptor.DataDescriptorSignature:
descriptor = new DataDescriptor(logEntryBuffer, dataPos);
dataPos += LogSectorSize;
break;
default:
entry = null;
return false;
}

descriptor.ReadFrom(logEntryBuffer, offset);
if (!descriptor.IsValid(header.SequenceNumber))
{
case Descriptor.ZeroDescriptorSignature:
descriptor = new ZeroDescriptor();
break;
case Descriptor.DataDescriptorSignature:
descriptor = new DataDescriptor(logEntryBuffer, dataPos);
dataPos += LogSectorSize;
break;
default:
entry = null;
return false;
}
}

descriptors.Add(descriptor);
descriptor.ReadFrom(logEntryBuffer, offset);
if (!descriptor.IsValid(header.SequenceNumber))
{
entry = null;
return false;
}

entry = new LogEntry(position, header, descriptors);
return true;
}
finally
{
ArrayPool<byte>.Shared.Return(logEntryBuffer);
descriptors.Add(descriptor);
}

entry = new LogEntry(position, header, descriptors);
return true;
}
finally
{
ArrayPool<byte>.Shared.Return(sectorBuffer);
ArrayPool<byte>.Shared.Return(logEntryBuffer);
}
}

Expand Down
29 changes: 13 additions & 16 deletions Library/DiscUtils.Vhdx/LogEntryHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,18 @@ namespace DiscUtils.Vhdx;
internal sealed class LogEntryHeader : IByteArraySerializable
{
public const uint LogEntrySignature = 0x65676F6C;
public const int ByteCount = 64;

//private byte[] _data;
public uint Checksum;
public uint DescriptorCount;
public uint EntryLength;
public ulong FlushedFileOffset;
public ulong LastFileOffset;
public Guid LogGuid;
public uint Reserved;
public ulong SequenceNumber;

public uint Signature;
public uint Tail;
public uint Checksum { get; private set; }
public uint DescriptorCount { get; private set; }
public uint EntryLength { get; private set; }
public ulong FlushedFileOffset { get; private set; }
public ulong LastFileOffset { get; private set; }
public Guid LogGuid { get; private set; }
public uint Reserved { get; private set; }
public ulong SequenceNumber { get; private set; }
public uint Signature { get; private set; }
public uint Tail { get; private set; }

public bool IsValid
{
Expand All @@ -49,13 +48,11 @@ public bool IsValid

public int Size
{
get { return 64; }
get { return ByteCount; }
}

public int ReadFrom(ReadOnlySpan<byte> buffer)
{
//_data = buffer.Slice(0, Size).ToArray();

Signature = EndianUtilities.ToUInt32LittleEndian(buffer);
Checksum = EndianUtilities.ToUInt32LittleEndian(buffer.Slice(4));
EntryLength = EndianUtilities.ToUInt32LittleEndian(buffer.Slice(8));
Expand All @@ -67,7 +64,7 @@ public int ReadFrom(ReadOnlySpan<byte> buffer)
FlushedFileOffset = EndianUtilities.ToUInt64LittleEndian(buffer.Slice(48));
LastFileOffset = EndianUtilities.ToUInt64LittleEndian(buffer.Slice(56));

return Size;
return ByteCount;
}

void IByteArraySerializable.WriteTo(Span<byte> buffer)
Expand Down

0 comments on commit 59e6430

Please sign in to comment.