diff --git a/Library/DiscUtils.Vhdx/LogEntry.cs b/Library/DiscUtils.Vhdx/LogEntry.cs index 2153ec8d4..0b97ed54d 100644 --- a/Library/DiscUtils.Vhdx/LogEntry.cs +++ b/Library/DiscUtils.Vhdx/LogEntry.cs @@ -33,9 +33,9 @@ namespace DiscUtils.Vhdx; internal sealed class LogEntry { - public const int LogSectorSize = (int)(4 * Sizes.OneKiB); - private readonly List _descriptors = new List(); + public const int LogSectorSize = 4 * Sizes.OneKiB; + private readonly List _descriptors = new List(); private readonly LogEntryHeader _header; private LogEntry(long position, LogEntryHeader header, List descriptors) @@ -101,90 +101,84 @@ public static bool TryRead(Stream logStream, out LogEntry entry) { var position = logStream.Position; - var sectorBuffer = ArrayPool.Shared.Rent(LogSectorSize); + var bytesToRead = LogEntryHeader.ByteCount; + Span 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.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.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(); + for (var i = 0; i < header.DescriptorCount; ++i) + { + var offset = i * 32 + 64; + Descriptor descriptor; - var descriptors = new List(); - 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.Shared.Return(logEntryBuffer); + descriptors.Add(descriptor); } + + entry = new LogEntry(position, header, descriptors); + return true; } finally { - ArrayPool.Shared.Return(sectorBuffer); + ArrayPool.Shared.Return(logEntryBuffer); } } diff --git a/Library/DiscUtils.Vhdx/LogEntryHeader.cs b/Library/DiscUtils.Vhdx/LogEntryHeader.cs index 8a59f1431..5e4052d47 100644 --- a/Library/DiscUtils.Vhdx/LogEntryHeader.cs +++ b/Library/DiscUtils.Vhdx/LogEntryHeader.cs @@ -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 { @@ -49,13 +48,11 @@ public bool IsValid public int Size { - get { return 64; } + get { return ByteCount; } } public int ReadFrom(ReadOnlySpan buffer) { - //_data = buffer.Slice(0, Size).ToArray(); - Signature = EndianUtilities.ToUInt32LittleEndian(buffer); Checksum = EndianUtilities.ToUInt32LittleEndian(buffer.Slice(4)); EntryLength = EndianUtilities.ToUInt32LittleEndian(buffer.Slice(8)); @@ -67,7 +64,7 @@ public int ReadFrom(ReadOnlySpan buffer) FlushedFileOffset = EndianUtilities.ToUInt64LittleEndian(buffer.Slice(48)); LastFileOffset = EndianUtilities.ToUInt64LittleEndian(buffer.Slice(56)); - return Size; + return ByteCount; } void IByteArraySerializable.WriteTo(Span buffer)