mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-22 23:15:20 +00:00
Merge branch 'master' into adam/cleanup
# Conflicts: # src/SharpCompress/Common/EntryStream.cs
This commit is contained in:
@@ -153,12 +153,12 @@ public partial class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, Sev
|
||||
}
|
||||
}
|
||||
|
||||
protected override EntryStream GetEntryStream()
|
||||
protected override EntryStream GetEntryStream(bool useSyncOverAsyncDispose)
|
||||
{
|
||||
var entry = _currentEntry.NotNull("currentEntry is not null");
|
||||
if (entry.IsDirectory)
|
||||
{
|
||||
return CreateEntryStream(Stream.Null);
|
||||
return CreateEntryStream(Stream.Null, false);
|
||||
}
|
||||
|
||||
var folder = entry.FilePart.Folder;
|
||||
@@ -186,7 +186,8 @@ public partial class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, Sev
|
||||
return CreateEntryStream(
|
||||
new SyncOnlyStream(
|
||||
new ReadOnlySubStream(_currentFolderStream, entry.Size, leaveOpen: true)
|
||||
)
|
||||
),
|
||||
useSyncOverAsyncDispose
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,13 @@ public partial class EntryStream : Stream
|
||||
private readonly Stream _stream;
|
||||
private bool _completed;
|
||||
private bool _isDisposed;
|
||||
private readonly bool _useSyncOverAsyncDispose;
|
||||
|
||||
internal EntryStream(IReader reader, Stream stream)
|
||||
internal EntryStream(IReader reader, Stream stream, bool useSyncOverAsyncDispose)
|
||||
{
|
||||
_reader = reader;
|
||||
_stream = stream;
|
||||
_useSyncOverAsyncDispose = useSyncOverAsyncDispose;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,7 +41,14 @@ public partial class EntryStream : Stream
|
||||
_isDisposed = true;
|
||||
if (!(_completed || _reader.Cancelled))
|
||||
{
|
||||
SkipEntry();
|
||||
if (_useSyncOverAsyncDispose)
|
||||
{
|
||||
SkipEntryAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
SkipEntry();
|
||||
}
|
||||
}
|
||||
|
||||
//Need a safe standard approach to this - it's okay for compression to overreads. Handling needs to be standardised
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.Tar.Headers;
|
||||
|
||||
namespace SharpCompress.Common.Tar;
|
||||
@@ -23,10 +25,28 @@ internal sealed class TarFilePart : FilePart
|
||||
if (_seekableStream != null)
|
||||
{
|
||||
_seekableStream.Position = Header.DataStartPosition ?? 0;
|
||||
return new TarReadOnlySubStream(_seekableStream, Header.Size);
|
||||
return new TarReadOnlySubStream(_seekableStream, Header.Size, false);
|
||||
}
|
||||
return Header.PackedStream.NotNull();
|
||||
}
|
||||
|
||||
internal override ValueTask<Stream?> GetCompressedStreamAsync(
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (_seekableStream != null)
|
||||
{
|
||||
var useSyncOverAsync = false;
|
||||
#if LEGACY_DOTNET
|
||||
useSyncOverAsync = true;
|
||||
#endif
|
||||
_seekableStream.Position = Header.DataStartPosition ?? 0;
|
||||
return new ValueTask<Stream?>(
|
||||
new TarReadOnlySubStream(_seekableStream, Header.Size, useSyncOverAsync)
|
||||
);
|
||||
}
|
||||
return new ValueTask<Stream?>(Header.PackedStream.NotNull());
|
||||
}
|
||||
|
||||
internal override Stream? GetRawStream() => null;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,15 @@ internal static partial class TarHeaderFactory
|
||||
break;
|
||||
case StreamingMode.Streaming:
|
||||
{
|
||||
header.PackedStream = new TarReadOnlySubStream(stream, header.Size);
|
||||
var useSyncOverAsync = false;
|
||||
#if LEGACY_DOTNET
|
||||
useSyncOverAsync = true;
|
||||
#endif
|
||||
header.PackedStream = new TarReadOnlySubStream(
|
||||
stream,
|
||||
header.Size,
|
||||
useSyncOverAsync
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -37,7 +37,11 @@ internal static partial class TarHeaderFactory
|
||||
break;
|
||||
case StreamingMode.Streaming:
|
||||
{
|
||||
header.PackedStream = new TarReadOnlySubStream(stream, header.Size);
|
||||
header.PackedStream = new TarReadOnlySubStream(
|
||||
stream,
|
||||
header.Size,
|
||||
false
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -6,13 +6,15 @@ namespace SharpCompress.Common.Tar;
|
||||
internal class TarReadOnlySubStream : Stream
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private readonly bool _useSyncOverAsyncDispose;
|
||||
|
||||
private bool _isDisposed;
|
||||
private long _amountRead;
|
||||
|
||||
public TarReadOnlySubStream(Stream stream, long bytesToRead)
|
||||
public TarReadOnlySubStream(Stream stream, long bytesToRead, bool useSyncOverAsyncDispose)
|
||||
{
|
||||
_stream = stream;
|
||||
_useSyncOverAsyncDispose = useSyncOverAsyncDispose;
|
||||
BytesLeftToRead = bytesToRead;
|
||||
}
|
||||
|
||||
@@ -35,7 +37,14 @@ internal class TarReadOnlySubStream : Stream
|
||||
|
||||
if (bytesInLastBlock != 0)
|
||||
{
|
||||
_stream.Skip(512 - bytesInLastBlock);
|
||||
if (_useSyncOverAsyncDispose)
|
||||
{
|
||||
_stream.SkipAsync(512 - bytesInLastBlock).GetAwaiter().GetResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
_stream.Skip(512 - bytesInLastBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
@@ -18,15 +19,18 @@ internal partial class WinzipAesCryptoStream : Stream
|
||||
private bool _isFinalBlock;
|
||||
private long _totalBytesLeftToRead;
|
||||
private bool _isDisposed;
|
||||
private bool _useSyncOverAsyncDispose;
|
||||
|
||||
internal WinzipAesCryptoStream(
|
||||
Stream stream,
|
||||
WinzipAesEncryptionData winzipAesEncryptionData,
|
||||
long length
|
||||
long length,
|
||||
bool useSyncOverAsyncDispose
|
||||
)
|
||||
{
|
||||
_stream = stream;
|
||||
_totalBytesLeftToRead = length;
|
||||
_useSyncOverAsyncDispose = useSyncOverAsyncDispose;
|
||||
|
||||
_cipher = CreateCipher(winzipAesEncryptionData);
|
||||
|
||||
@@ -68,20 +72,33 @@ internal partial class WinzipAesCryptoStream : Stream
|
||||
if (disposing)
|
||||
{
|
||||
// Read out last 10 auth bytes - catch exceptions for async-only streams
|
||||
try
|
||||
if (_useSyncOverAsyncDispose)
|
||||
{
|
||||
var ten = ArrayPool<byte>.Shared.Rent(10);
|
||||
try
|
||||
{
|
||||
_stream.ReadFullyAsync(ten, 0, 10).GetAwaiter().GetResult();
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(ten);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Span<byte> ten = stackalloc byte[10];
|
||||
_stream.ReadFully(ten);
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
// Stream may be async-only, auth bytes will be skipped
|
||||
// This is acceptable when the entire stream has been read
|
||||
}
|
||||
_stream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReadAuthBytesAsync()
|
||||
{
|
||||
byte[] authBytes = new byte[10];
|
||||
await _stream.ReadFullyAsync(authBytes, 0, 10).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public override void Flush() { }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
|
||||
@@ -99,10 +99,15 @@ internal abstract partial class ZipFilePart
|
||||
{
|
||||
if (Header.WinzipAesEncryptionData != null)
|
||||
{
|
||||
var useSyncOverAsync = false;
|
||||
#if LEGACY_DOTNET
|
||||
useSyncOverAsync = true;
|
||||
#endif
|
||||
return new WinzipAesCryptoStream(
|
||||
plainStream,
|
||||
Header.WinzipAesEncryptionData,
|
||||
Header.CompressedSize - 10
|
||||
Header.CompressedSize - 10,
|
||||
useSyncOverAsync
|
||||
);
|
||||
}
|
||||
return plainStream;
|
||||
|
||||
@@ -270,7 +270,8 @@ internal abstract partial class ZipFilePart : FilePart
|
||||
return new WinzipAesCryptoStream(
|
||||
plainStream,
|
||||
Header.WinzipAesEncryptionData,
|
||||
Header.CompressedSize - 10
|
||||
Header.CompressedSize - 10,
|
||||
false
|
||||
);
|
||||
}
|
||||
return plainStream;
|
||||
|
||||
@@ -178,7 +178,11 @@ public abstract partial class AbstractReader<TEntry, TVolume>
|
||||
.Parts.First()
|
||||
.GetCompressedStreamAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return CreateEntryStream(stream);
|
||||
var useSyncOverAsync = false;
|
||||
#if LEGACY_DOTNET
|
||||
useSyncOverAsync = true;
|
||||
#endif
|
||||
return CreateEntryStream(stream, useSyncOverAsync);
|
||||
}
|
||||
|
||||
internal virtual ValueTask<bool> NextEntryForCurrentStreamAsync() =>
|
||||
|
||||
@@ -35,8 +35,6 @@ public abstract partial class AbstractReader<TEntry, TVolume> : IReader, IAsyncR
|
||||
|
||||
public ArchiveType ArchiveType { get; }
|
||||
|
||||
protected bool IsAsync => _entriesForCurrentReadStreamAsync is not null;
|
||||
|
||||
/// <summary>
|
||||
/// Current volume that the current entry resides in
|
||||
/// </summary>
|
||||
@@ -248,7 +246,7 @@ public abstract partial class AbstractReader<TEntry, TVolume> : IReader, IAsyncR
|
||||
{
|
||||
throw new ArgumentException("WriteEntryTo or OpenEntryStream can only be called once.");
|
||||
}
|
||||
var stream = GetEntryStream();
|
||||
var stream = GetEntryStream(false);
|
||||
_wroteCurrentEntry = true;
|
||||
return stream;
|
||||
}
|
||||
@@ -256,11 +254,11 @@ public abstract partial class AbstractReader<TEntry, TVolume> : IReader, IAsyncR
|
||||
/// <summary>
|
||||
/// Retains a reference to the entry stream, so we can check whether it completed later.
|
||||
/// </summary>
|
||||
protected EntryStream CreateEntryStream(Stream? decompressed) =>
|
||||
new(this, decompressed.NotNull());
|
||||
protected EntryStream CreateEntryStream(Stream? decompressed, bool useSyncOverAsyncDispose) =>
|
||||
new(this, decompressed.NotNull(), useSyncOverAsyncDispose);
|
||||
|
||||
protected virtual EntryStream GetEntryStream() =>
|
||||
CreateEntryStream(Entry.Parts.First().GetCompressedStream());
|
||||
protected virtual EntryStream GetEntryStream(bool useSyncOverAsyncDispose) =>
|
||||
CreateEntryStream(Entry.Parts.First().GetCompressedStream(), useSyncOverAsyncDispose);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -27,6 +27,10 @@ public abstract partial class RarReader
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var useSyncOverAsync = false;
|
||||
#if LEGACY_DOTNET
|
||||
useSyncOverAsync = true;
|
||||
#endif
|
||||
if (Entry.IsRedir)
|
||||
{
|
||||
throw new InvalidOperationException("no stream for redirect entry");
|
||||
@@ -40,7 +44,8 @@ public abstract partial class RarReader
|
||||
return CreateEntryStream(
|
||||
await RarCrcStream
|
||||
.CreateAsync(UnpackV1.Value, Entry.FileHeader, stream, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
.ConfigureAwait(false),
|
||||
useSyncOverAsync
|
||||
);
|
||||
}
|
||||
|
||||
@@ -49,14 +54,16 @@ public abstract partial class RarReader
|
||||
return CreateEntryStream(
|
||||
await RarBLAKE2spStream
|
||||
.CreateAsync(UnpackV2017.Value, Entry.FileHeader, stream, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
.ConfigureAwait(false),
|
||||
useSyncOverAsync
|
||||
);
|
||||
}
|
||||
|
||||
return CreateEntryStream(
|
||||
await RarCrcStream
|
||||
.CreateAsync(UnpackV2017.Value, Entry.FileHeader, stream, cancellationToken)
|
||||
.ConfigureAwait(false)
|
||||
.ConfigureAwait(false),
|
||||
useSyncOverAsync
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ public abstract partial class RarReader : AbstractReader<RarReaderEntry, RarVolu
|
||||
protected virtual IEnumerable<FilePart> CreateFilePartEnumerableForCurrentEntry() =>
|
||||
Entry.Parts;
|
||||
|
||||
protected override EntryStream GetEntryStream()
|
||||
protected override EntryStream GetEntryStream(bool useSyncOverAsyncDispose)
|
||||
{
|
||||
if (Entry.IsRedir)
|
||||
{
|
||||
@@ -122,17 +122,24 @@ public abstract partial class RarReader : AbstractReader<RarReaderEntry, RarVolu
|
||||
);
|
||||
if (Entry.IsRarV3)
|
||||
{
|
||||
return CreateEntryStream(RarCrcStream.Create(UnpackV1.Value, Entry.FileHeader, stream));
|
||||
return CreateEntryStream(
|
||||
RarCrcStream.Create(UnpackV1.Value, Entry.FileHeader, stream),
|
||||
useSyncOverAsyncDispose
|
||||
);
|
||||
}
|
||||
|
||||
if (Entry.FileHeader.FileCrc?.Length > 5)
|
||||
{
|
||||
return CreateEntryStream(
|
||||
RarBLAKE2spStream.Create(UnpackV2017.Value, Entry.FileHeader, stream)
|
||||
RarBLAKE2spStream.Create(UnpackV2017.Value, Entry.FileHeader, stream),
|
||||
useSyncOverAsyncDispose
|
||||
);
|
||||
}
|
||||
|
||||
return CreateEntryStream(RarCrcStream.Create(UnpackV2017.Value, Entry.FileHeader, stream));
|
||||
return CreateEntryStream(
|
||||
RarCrcStream.Create(UnpackV2017.Value, Entry.FileHeader, stream),
|
||||
useSyncOverAsyncDispose
|
||||
);
|
||||
}
|
||||
|
||||
// GetEntryStreamAsync moved to RarReader.Async.cs
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
@@ -252,14 +253,14 @@ public class ZipReaderAsyncTests : ReaderTests
|
||||
File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.WinzipAES.zip"))
|
||||
)
|
||||
)
|
||||
using (
|
||||
IReader baseReader = ZipReader.OpenReader(
|
||||
|
||||
await using (
|
||||
var reader = await ReaderFactory.OpenAsyncReader(
|
||||
stream,
|
||||
new ReaderOptions { Password = "test" }
|
||||
)
|
||||
)
|
||||
{
|
||||
IAsyncReader reader = (IAsyncReader)baseReader;
|
||||
while (await reader.MoveToNextEntryAsync())
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
@@ -284,14 +285,13 @@ public class ZipReaderAsyncTests : ReaderTests
|
||||
File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "zipcrypto.zip"))
|
||||
)
|
||||
)
|
||||
using (
|
||||
IReader baseReader = ZipReader.OpenReader(
|
||||
await using (
|
||||
var reader = await ReaderFactory.OpenAsyncReader(
|
||||
stream,
|
||||
new ReaderOptions { Password = "test" }
|
||||
)
|
||||
)
|
||||
{
|
||||
IAsyncReader reader = (IAsyncReader)baseReader;
|
||||
while (await reader.MoveToNextEntryAsync())
|
||||
{
|
||||
if (!reader.Entry.IsDirectory)
|
||||
|
||||
Reference in New Issue
Block a user