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
14 changes: 14 additions & 0 deletions src/SharpCompress/Archives/Rar/RarArchive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ public override void Dispose()
}
}

public override async ValueTask DisposeAsync()
{
if (!_disposed)
{
if (UnpackV1.IsValueCreated && UnpackV1.Value is IDisposable unpackV1)
{
unpackV1.Dispose();
}

_disposed = true;
await base.DisposeAsync();
}
}

protected override IEnumerable<RarArchiveEntry> LoadEntries(IEnumerable<RarVolume> volumes) =>
RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions);

Expand Down
16 changes: 12 additions & 4 deletions src/SharpCompress/Common/AsyncBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,19 @@ public async ValueTask<ulong> ReadUInt64Async(CancellationToken ct = default)
return BinaryPrimitives.ReadUInt64LittleEndian(_buffer);
}

public async ValueTask<byte[]> ReadBytesAsync(int count, CancellationToken ct = default)
public async ValueTask ReadBytesAsync(
byte[] bytes,
int offset,
int count,
CancellationToken ct = default
)
{
var result = new byte[count];
await _stream.ReadExactAsync(result, 0, count, ct).ConfigureAwait(false);
return result;
await _stream.ReadExactAsync(bytes, offset, count, ct).ConfigureAwait(false);
}

public async ValueTask SkipAsync(int count, CancellationToken ct = default)
{
await _stream.SkipAsync(count, ct).ConfigureAwait(false);
}

public void Dispose()
Expand Down
3 changes: 2 additions & 1 deletion src/SharpCompress/Common/Zip/Headers/DirectoryEndHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ internal override async ValueTask Read(AsyncBinaryReader reader)
DirectorySize = await reader.ReadUInt32Async();
DirectoryStartOffsetRelativeToDisk = await reader.ReadUInt32Async();
CommentLength = await reader.ReadUInt16Async();
Comment = await reader.ReadBytesAsync(CommentLength);
Comment = new byte[CommentLength];
await reader.ReadBytesAsync(Comment, 0, CommentLength);
}

public ushort VolumeNumber { get; private set; }
Expand Down
10 changes: 6 additions & 4 deletions src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ internal override async ValueTask Read(AsyncBinaryReader reader)
InternalFileAttributes = await reader.ReadUInt16Async();
ExternalFileAttributes = await reader.ReadUInt32Async();
RelativeOffsetOfEntryHeader = await reader.ReadUInt32Async();

var name = await reader.ReadBytesAsync(nameLength);
var extra = await reader.ReadBytesAsync(extraLength);
var comment = await reader.ReadBytesAsync(commentLength);
var name = new byte[nameLength];
var extra = new byte[extraLength];
var comment = new byte[commentLength];
await reader.ReadBytesAsync(name, 0, nameLength);
await reader.ReadBytesAsync(extra, 0, extraLength);
await reader.ReadBytesAsync(comment, 0, commentLength);

ProcessReadData(name, extra, comment);
}
Expand Down
6 changes: 4 additions & 2 deletions src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ internal override async ValueTask Read(AsyncBinaryReader reader)
UncompressedSize = await reader.ReadUInt32Async();
var nameLength = await reader.ReadUInt16Async();
var extraLength = await reader.ReadUInt16Async();
var name = await reader.ReadBytesAsync(nameLength);
var extra = await reader.ReadBytesAsync(extraLength);
var name = new byte[nameLength];
var extra = new byte[extraLength];
await reader.ReadBytesAsync(name, 0, nameLength);
await reader.ReadBytesAsync(extra, 0, extraLength);

ProcessReadData(name, extra);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,11 @@ internal override async ValueTask Read(AsyncBinaryReader reader)
TotalNumberOfEntries = (long)await reader.ReadUInt64Async();
DirectorySize = (long)await reader.ReadUInt64Async();
DirectoryStartOffsetRelativeToDisk = (long)await reader.ReadUInt64Async();
DataSector = await reader.ReadBytesAsync(
(int)(
SizeOfDirectoryEndRecord
- SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS
)
var size = (int)(
SizeOfDirectoryEndRecord - SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS
);
DataSector = new byte[size];
await reader.ReadBytesAsync(DataSector, 0, size);
}

private const int SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS = 44;
Expand Down
111 changes: 25 additions & 86 deletions src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
Expand All @@ -16,6 +17,8 @@ internal sealed class SeekableZipHeaderFactory : ZipHeaderFactory
private const int MAX_SEARCH_LENGTH_FOR_EOCD = 65557;
private bool _zip64;

private static readonly byte[] needle = { 0x06, 0x05, 0x4b, 0x50 };

internal SeekableZipHeaderFactory(string? password, IArchiveEncoding archiveEncoding)
: base(StreamingMode.Seekable, password, archiveEncoding) { }

Expand Down Expand Up @@ -153,74 +156,7 @@ internal IEnumerable<ZipHeader> ReadSeekableHeader(Stream stream, bool useSync)
}
}

internal async IAsyncEnumerable<ZipHeader> ReadSeekableHeaderAsync(Stream stream, bool useSync)
{
var reader = new AsyncBinaryReader(stream);

await SeekBackToHeaderAsync(stream, reader);

var eocd_location = stream.Position;
var entry = new DirectoryEndHeader();
await entry.Read(reader);

if (entry.IsZip64)
{
_zip64 = true;

// ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR should be before the EOCD
stream.Seek(eocd_location - ZIP64_EOCD_LENGTH - 4, SeekOrigin.Begin);
var zip64_locator = await reader.ReadUInt32Async();
if (zip64_locator != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR)
{
throw new ArchiveException("Failed to locate the Zip64 Directory Locator");
}

var zip64Locator = new Zip64DirectoryEndLocatorHeader();
await zip64Locator.Read(reader);

stream.Seek(zip64Locator.RelativeOffsetOfTheEndOfDirectoryRecord, SeekOrigin.Begin);
var zip64Signature = await reader.ReadUInt32Async();
if (zip64Signature != ZIP64_END_OF_CENTRAL_DIRECTORY)
{
throw new ArchiveException("Failed to locate the Zip64 Header");
}

var zip64Entry = new Zip64DirectoryEndHeader();
await zip64Entry.Read(reader);
stream.Seek(zip64Entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin);
}
else
{
stream.Seek(entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin);
}

var position = stream.Position;
while (true)
{
stream.Position = position;
var signature = await reader.ReadUInt32Async();
var nextHeader = await ReadHeader(signature, reader, _zip64);
position = stream.Position;

if (nextHeader is null)
{
yield break;
}

if (nextHeader is DirectoryEntryHeader entryHeader)
{
//entry could be zero bytes so we need to know that.
entryHeader.HasData = entryHeader.CompressedSize != 0;
yield return entryHeader;
}
else if (nextHeader is DirectoryEndHeader endHeader)
{
yield return endHeader;
}
}
}

private static bool IsMatch(byte[] haystack, int position, byte[] needle)
private static bool IsMatch(Span<byte> haystack, int position, byte[] needle)
Comment thread
adamhathcock marked this conversation as resolved.
{
for (var i = 0; i < needle.Length; i++)
{
Expand All @@ -247,29 +183,35 @@ private static async ValueTask SeekBackToHeaderAsync(Stream stream, AsyncBinaryR
stream.Length < MAX_SEARCH_LENGTH_FOR_EOCD
? (int)stream.Length
: MAX_SEARCH_LENGTH_FOR_EOCD;
// We search for marker in reverse to find the first occurance
byte[] needle = { 0x06, 0x05, 0x4b, 0x50 };

stream.Seek(-len, SeekOrigin.End);
var seek = ArrayPool<byte>.Shared.Rent(len);

var seek = await reader.ReadBytesAsync(len);

// Search in reverse
Array.Reverse(seek);
try
{
await reader.ReadBytesAsync(seek, 0, len, default);
var memory = new Memory<byte>(seek, 0, len);
var span = memory.Span;
span.Reverse();

// don't exclude the minimum eocd region, otherwise you fail to locate the header in empty zip files
var max_search_area = len; // - MINIMUM_EOCD_LENGTH;
// don't exclude the minimum eocd region, otherwise you fail to locate the header in empty zip files
var max_search_area = len; // - MINIMUM_EOCD_LENGTH;

for (var pos_from_end = 0; pos_from_end < max_search_area; ++pos_from_end)
{
if (IsMatch(seek, pos_from_end, needle))
for (var pos_from_end = 0; pos_from_end < max_search_area; ++pos_from_end)
{
stream.Seek(-pos_from_end, SeekOrigin.End);
return;
if (IsMatch(span, pos_from_end, needle))
{
stream.Seek(-pos_from_end, SeekOrigin.End);
return;
}
}
}

throw new ArchiveException("Failed to locate the Zip Header");
throw new ArchiveException("Failed to locate the Zip Header");
}
finally
{
ArrayPool<byte>.Shared.Return(seek);
}
}

private static void SeekBackToHeader(Stream stream, BinaryReader reader)
Expand All @@ -286,9 +228,6 @@ private static void SeekBackToHeader(Stream stream, BinaryReader reader)
stream.Length < MAX_SEARCH_LENGTH_FOR_EOCD
? (int)stream.Length
: MAX_SEARCH_LENGTH_FOR_EOCD;
// We search for marker in reverse to find the first occurance
byte[] needle = { 0x06, 0x05, 0x4b, 0x50 };

stream.Seek(-len, SeekOrigin.End);

var seek = reader.ReadBytes(len);
Expand Down
2 changes: 1 addition & 1 deletion src/SharpCompress/Common/Zip/ZipHeaderFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ IArchiveEncoding archiveEncoding
}
else
{
await reader.ReadBytesAsync(zip64 ? 20 : 12);
await reader.SkipAsync(zip64 ? 20 : 12);
}
return null;
}
Expand Down
Loading
Loading