Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant.

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)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This data availability check would be broken when working within a stream starting at an offset > 0. ReadMaximum() below does the availability check correctly.

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