From a8f6f188438091800a093866cf318f244c39b985 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Fri, 31 Jul 2026 14:21:24 +0100 Subject: [PATCH 1/2] Fix SeekableSharpCompressStream.StopRecording() to rewind to recorded position When SeekableSharpCompressStream.StopRecording() was called (e.g. after the tar probe in GZipFactory.TryOpenReader), it cleared _recordedPosition without seeking back to it. This left the underlying FileStream at the advanced position reached during the probe, so the subsequent GZipReader would start reading from the wrong offset, producing Key=null and 0 extracted bytes. Fix: seek back to _recordedPosition before clearing it, matching the behavior of the non-seekable SharpCompressStream.StopRecording() which rewinds _logicalPosition. Also enhance the existing GZip_ReaderFactory_FlatGZip test to actually verify the extracted content (not just MoveToNextEntry returns true), and add an async variant. Closes #1391 --- .../IO/SeekableSharpCompressStream.cs | 11 ++++++- .../GZip/GZipReaderAsyncTests.cs | 30 +++++++++++++++++++ .../GZip/GZipReaderTests.cs | 6 ++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/SharpCompress/IO/SeekableSharpCompressStream.cs b/src/SharpCompress/IO/SeekableSharpCompressStream.cs index bad123bb2..e3aa9bb5c 100644 --- a/src/SharpCompress/IO/SeekableSharpCompressStream.cs +++ b/src/SharpCompress/IO/SeekableSharpCompressStream.cs @@ -83,7 +83,16 @@ public override void Rewind(bool stopRecording = false) public override void StartRecording(int? minBufferSize = null) => _recordedPosition = _stream.Position; - public override void StopRecording() => _recordedPosition = null; + public override void StopRecording() + { + if (_recordedPosition.HasValue) + { + // Seek back to the recording anchor position, matching the behavior of the + // non-seekable SharpCompressStream.StopRecording() which rewinds _logicalPosition. + _stream.Seek(_recordedPosition.Value, SeekOrigin.Begin); + } + _recordedPosition = null; + } protected override void Dispose(bool disposing) { diff --git a/tests/SharpCompress.Test/GZip/GZipReaderAsyncTests.cs b/tests/SharpCompress.Test/GZip/GZipReaderAsyncTests.cs index 83d2b352b..a3da7e049 100644 --- a/tests/SharpCompress.Test/GZip/GZipReaderAsyncTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipReaderAsyncTests.cs @@ -1,4 +1,5 @@ using System.IO; +using System.IO.Compression; using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.IO; @@ -37,4 +38,33 @@ public async ValueTask GZip_Reader_Generic2_Async() } } } + + [Fact] + public async ValueTask GZip_ReaderFactory_FlatGZip_Async() + { + var source = new byte[2048]; + for (var i = 0; i < source.Length; i++) + { + source[i] = 0xFF; + } + + var gzipPath = Path.Combine(SCRATCH_FILES_PATH, "Flat.bin.gz"); + using (var output = File.Create(gzipPath)) + await using (var gzip = new GZipStream(output, CompressionMode.Compress)) + { + await gzip.WriteAsync(source, 0, source.Length); + } + + await using Stream stream = File.OpenRead(gzipPath); + await using var reader = await ReaderFactory.OpenAsyncReader(stream); + Assert.IsType(reader); + Assert.True(await reader.MoveToNextEntryAsync()); + + using var ms = new MemoryStream(); + await reader.WriteEntryToAsync(ms); + Assert.Equal(source.Length, ms.Length); + Assert.Equal(source, ms.ToArray()); + + Assert.False(await reader.MoveToNextEntryAsync()); + } } diff --git a/tests/SharpCompress.Test/GZip/GZipReaderTests.cs b/tests/SharpCompress.Test/GZip/GZipReaderTests.cs index fb3936a8f..ab870da2d 100644 --- a/tests/SharpCompress.Test/GZip/GZipReaderTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipReaderTests.cs @@ -48,6 +48,12 @@ public void GZip_ReaderFactory_FlatGZip() using var reader = ReaderFactory.OpenReader(stream); Assert.IsType(reader); Assert.True(reader.MoveToNextEntry()); + + using var ms = new MemoryStream(); + reader.WriteEntryTo(ms); + Assert.Equal(source.Length, ms.Length); + Assert.Equal(source, ms.ToArray()); + Assert.False(reader.MoveToNextEntry()); } } From 82c44cb5a5f9299418f37d171adb1b83d3add772 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Fri, 31 Jul 2026 14:52:58 +0100 Subject: [PATCH 2/2] remove async dispose --- tests/SharpCompress.Test/GZip/GZipReaderAsyncTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/SharpCompress.Test/GZip/GZipReaderAsyncTests.cs b/tests/SharpCompress.Test/GZip/GZipReaderAsyncTests.cs index a3da7e049..989147809 100644 --- a/tests/SharpCompress.Test/GZip/GZipReaderAsyncTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipReaderAsyncTests.cs @@ -50,12 +50,12 @@ public async ValueTask GZip_ReaderFactory_FlatGZip_Async() var gzipPath = Path.Combine(SCRATCH_FILES_PATH, "Flat.bin.gz"); using (var output = File.Create(gzipPath)) - await using (var gzip = new GZipStream(output, CompressionMode.Compress)) + using (var gzip = new GZipStream(output, CompressionMode.Compress)) { await gzip.WriteAsync(source, 0, source.Length); } - await using Stream stream = File.OpenRead(gzipPath); + using Stream stream = File.OpenRead(gzipPath); await using var reader = await ReaderFactory.OpenAsyncReader(stream); Assert.IsType(reader); Assert.True(await reader.MoveToNextEntryAsync());