mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-11-15 17:02:03 +00:00
Merge pull request #1488 from adamhathcock/fix/1485-solid-7z-deflate
Fix solid 7z Deflate extraction corruption
This commit is contained in:
@@ -188,8 +188,12 @@ public partial class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, Sev
|
||||
);
|
||||
}
|
||||
|
||||
return CreateEntryStream(
|
||||
new ReadOnlySubStream(_currentFolderStream, entry.Size, leaveOpen: true)
|
||||
// Entry boundaries do not end a solid folder's decoder. Its buffered input must
|
||||
// remain intact until the reader advances to another folder or is disposed.
|
||||
return new EntryStream(
|
||||
this,
|
||||
new ReadOnlySubStream(_currentFolderStream, entry.Size, leaveOpen: true),
|
||||
rewindDecoderOnDispose: false
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ public partial class EntryStream
|
||||
}
|
||||
|
||||
//Need a safe standard approach to this - it's okay for compression to overreads. Handling needs to be standardised
|
||||
if (_stream is IStreamStack ss)
|
||||
if (_rewindDecoderOnDispose && _stream is IStreamStack ss)
|
||||
{
|
||||
if (ss.BaseStream() is SharpCompress.Compressors.Deflate.DeflateStream deflateStream)
|
||||
{
|
||||
|
||||
@@ -12,13 +12,15 @@ public partial class EntryStream : Stream
|
||||
{
|
||||
private readonly IReader _reader;
|
||||
private readonly Stream _stream;
|
||||
private readonly bool _rewindDecoderOnDispose;
|
||||
private bool _completed;
|
||||
private bool _isDisposed;
|
||||
|
||||
internal EntryStream(IReader reader, Stream stream)
|
||||
internal EntryStream(IReader reader, Stream stream, bool rewindDecoderOnDispose = true)
|
||||
{
|
||||
_reader = reader;
|
||||
_stream = stream;
|
||||
_rewindDecoderOnDispose = rewindDecoderOnDispose;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -54,7 +56,7 @@ public partial class EntryStream : Stream
|
||||
}
|
||||
|
||||
//Need a safe standard approach to this - it's okay for compression to overreads. Handling needs to be standardised
|
||||
if (_stream is IStreamStack ss)
|
||||
if (_rewindDecoderOnDispose && _stream is IStreamStack ss)
|
||||
{
|
||||
if (
|
||||
ss.GetStream<SharpCompress.Compressors.Deflate.DeflateStream>()
|
||||
|
||||
126
tests/SharpCompress.Test/SevenZip/SevenZipDeflateSolidTests.cs
Normal file
126
tests/SharpCompress.Test/SevenZip/SevenZipDeflateSolidTests.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives.SevenZip;
|
||||
using SharpCompress.Readers;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.SevenZip;
|
||||
|
||||
public class SevenZipDeflateSolidTests : TestBase
|
||||
{
|
||||
private static readonly int[] Sizes = [1000, 70000, 100000, 200000, 500000, 1048576, 3000000];
|
||||
|
||||
// Generated with libarchive bsdtar --format 7zip --options compression=deflate.
|
||||
// Each file repeats a 4096-byte xorshift sequence with a different starting offset.
|
||||
private static byte[] CreateExpected(int index)
|
||||
{
|
||||
var block = new byte[4096];
|
||||
uint state = 0x12345678;
|
||||
for (var i = 0; i < block.Length; i++)
|
||||
{
|
||||
state ^= state << 13;
|
||||
state ^= state >> 17;
|
||||
state ^= state << 5;
|
||||
block[i] = (byte)state;
|
||||
}
|
||||
|
||||
var data = new byte[Sizes[index]];
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
{
|
||||
data[i] = block[(i + index * 997) % block.Length];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(512, 0)]
|
||||
[InlineData(4096, 1)]
|
||||
[InlineData(65536, 2)]
|
||||
[InlineData(131072, 0)]
|
||||
[InlineData(300000, 0)]
|
||||
public void SevenZipArchive_Solid_Deflate_ExtractAllEntries(int bufferSize, int firstEntryMode)
|
||||
{
|
||||
using var archive = SevenZipArchive.OpenArchive(
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.Deflate.7z")
|
||||
);
|
||||
Assert.True(archive.IsSolid);
|
||||
using var reader = archive.ExtractAllEntries();
|
||||
var index = 0;
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
Assert.Equal($"{index}.bin", reader.Entry.Key);
|
||||
Assert.Equal(Sizes[index], reader.Entry.Size);
|
||||
if (index == 0 && firstEntryMode == 2)
|
||||
{
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
using var stream = reader.OpenEntryStream();
|
||||
if (index == 0 && firstEntryMode == 1)
|
||||
{
|
||||
Assert.True(stream.Read(new byte[17], 0, 17) > 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var output = new MemoryStream();
|
||||
stream.CopyTo(output, bufferSize);
|
||||
Assert.Equal(CreateExpected(index), output.ToArray());
|
||||
}
|
||||
index++;
|
||||
}
|
||||
Assert.Equal(Sizes.Length, index);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(512, 0)]
|
||||
[InlineData(4096, 1)]
|
||||
[InlineData(65536, 2)]
|
||||
[InlineData(131072, 0)]
|
||||
[InlineData(300000, 0)]
|
||||
public async Task SevenZipArchive_Solid_Deflate_ExtractAllEntries_Async(
|
||||
int bufferSize,
|
||||
int firstEntryMode
|
||||
)
|
||||
{
|
||||
await using var archive = await SevenZipArchive.OpenAsyncArchive(
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "7Zip.solid.Deflate.7z"),
|
||||
cancellationToken: CancellationToken.None
|
||||
);
|
||||
Assert.True(await archive.IsSolidAsync());
|
||||
await using var reader = await archive.ExtractAllEntriesAsync();
|
||||
var index = 0;
|
||||
while (await reader.MoveToNextEntryAsync(CancellationToken.None))
|
||||
{
|
||||
Assert.Equal($"{index}.bin", reader.Entry.Key);
|
||||
Assert.Equal(Sizes[index], reader.Entry.Size);
|
||||
if (index == 0 && firstEntryMode == 2)
|
||||
{
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
#if LEGACY_DOTNET
|
||||
using var stream = await reader.OpenEntryStreamAsync(CancellationToken.None);
|
||||
#else
|
||||
await using var stream = await reader.OpenEntryStreamAsync(CancellationToken.None);
|
||||
#endif
|
||||
if (index == 0 && firstEntryMode == 1)
|
||||
{
|
||||
Assert.True(
|
||||
await stream.ReadAsync(new byte[17], 0, 17, CancellationToken.None) > 0
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var output = new MemoryStream();
|
||||
await stream.CopyToAsync(output, bufferSize, CancellationToken.None);
|
||||
Assert.Equal(CreateExpected(index), output.ToArray());
|
||||
}
|
||||
index++;
|
||||
}
|
||||
Assert.Equal(Sizes.Length, index);
|
||||
}
|
||||
}
|
||||
BIN
tests/TestArchives/Archives/7Zip.solid.Deflate.7z
Normal file
BIN
tests/TestArchives/Archives/7Zip.solid.Deflate.7z
Normal file
Binary file not shown.
Reference in New Issue
Block a user