tar fixes

This commit is contained in:
Adam Hathcock
2026-01-20 12:22:38 +00:00
parent 8abb972f87
commit 2175cb299d
9 changed files with 131 additions and 39 deletions

View File

@@ -76,4 +76,30 @@ public class TarEntry : Entry
}
}
}
internal static async IAsyncEnumerable<TarEntry> GetEntriesAsync(
StreamingMode mode,
Stream stream,
CompressionType compressionType,
IArchiveEncoding archiveEncoding
)
{
await foreach (var header in TarHeaderFactory.ReadHeaderAsync(mode, stream, archiveEncoding))
{
if (header != null)
{
if (mode == StreamingMode.Seekable)
{
yield return new TarEntry(new TarFilePart(header, stream), compressionType);
}
else
{
yield return new TarEntry(new TarFilePart(header, null), compressionType);
}
}
else
{
throw new IncompleteArchiveException("Unexpected EOF reading tar file");
}
}
}
}

View File

@@ -278,6 +278,22 @@ public class DeflateStream : Stream, IStreamStack
}
}
#if !NETFRAMEWORK && !NETSTANDARD2_0
public override async ValueTask DisposeAsync()
{
if (!_disposed)
{
#if DEBUG_STREAMS
this.DebugDispose(typeof(DeflateStream));
#endif
await _baseStream.DisposeAsync().ConfigureAwait(false);
_disposed = true;
}
await base.DisposeAsync().ConfigureAwait(false);
}
#endif
/// <summary>
/// Flush the stream.
/// </summary>
@@ -299,24 +315,6 @@ public class DeflateStream : Stream, IStreamStack
await _baseStream.FlushAsync(cancellationToken).ConfigureAwait(false);
}
#if !NETFRAMEWORK && !NETSTANDARD2_0
public override async ValueTask DisposeAsync()
{
if (_disposed)
{
return;
}
_disposed = true;
if (_baseStream != null)
{
await _baseStream.DisposeAsync().ConfigureAwait(false);
}
#if DEBUG_STREAMS
this.DebugDispose(typeof(DeflateStream));
#endif
await base.DisposeAsync().ConfigureAwait(false);
}
#endif
/// <summary>
/// Read data from the stream.

View File

@@ -141,23 +141,23 @@ public class SharpCompressStream : Stream, IStreamStack
#if DEBUG_STREAMS
this.DebugDispose(typeof(SharpCompressStream));
#endif
if (_isDisposed)
{
return;
}
_isDisposed = true;
base.Dispose(disposing);
if (this.LeaveOpen)
{
return;
}
if (ThrowOnDispose)
{
throw new InvalidOperationException(
$"Attempt to dispose of a {nameof(SharpCompressStream)} when {nameof(ThrowOnDispose)} is {ThrowOnDispose}"
);
}
if (_isDisposed)
{
return;
}
_isDisposed = true;
base.Dispose(disposing);
if (disposing)
{
Stream.Dispose();
@@ -450,5 +450,36 @@ public class SharpCompressStream : Stream, IStreamStack
_internalPosition += buffer.Length;
}
public override async ValueTask DisposeAsync()
{
#if DEBUG_STREAMS
this.DebugDispose(typeof(SharpCompressStream));
#endif
if (this.LeaveOpen)
{
return;
}
if (ThrowOnDispose)
{
throw new InvalidOperationException(
$"Attempt to dispose of a {nameof(SharpCompressStream)} when {nameof(ThrowOnDispose)} is {ThrowOnDispose}"
);
}
if (_isDisposed)
{
return;
}
_isDisposed = true;
await base.DisposeAsync();
await Stream.DisposeAsync();
if (_buffer != null)
{
ArrayPool<byte>.Shared.Return(_buffer);
_buffer = null;
}
}
#endif
}

View File

@@ -119,4 +119,11 @@ public partial class TarReader : AbstractReader<TarEntry, TarVolume>
compressionType,
Options.ArchiveEncoding
);
protected override IAsyncEnumerable<TarEntry> GetEntriesAsync(Stream stream) =>
TarEntry.GetEntriesAsync(
StreamingMode.Streaming,
stream,
compressionType,
Options.ArchiveEncoding
);
}

View File

@@ -63,9 +63,4 @@ public class AsyncOnlyStream : SharpCompressStream
public override void Write(byte[] buffer, int offset, int count) =>
Stream.Write(buffer, offset, count);
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Threading.Tasks;
namespace SharpCompress.Test.Mocks;
@@ -46,4 +47,13 @@ public class FlushOnDisposeStream(Stream innerStream) : Stream
base.Dispose(disposing);
}
#if !NETFRAMEWORK && !NETSTANDARD2_0
public override async ValueTask DisposeAsync()
{
await innerStream.FlushAsync();
innerStream.Close();
await base.DisposeAsync();
}
#endif
}

View File

@@ -50,6 +50,14 @@ public class TestStream(Stream stream, bool read, bool write, bool seek) : Strea
Memory<byte> buffer,
CancellationToken cancellationToken = default
) => stream.ReadAsync(buffer, cancellationToken);
public override async ValueTask DisposeAsync()
{
await base.DisposeAsync();
await stream.DisposeAsync();
IsDisposed = true;
}
#endif
public override long Seek(long offset, SeekOrigin origin) => stream.Seek(offset, origin);

View File

@@ -160,13 +160,25 @@ public abstract class ReaderTests : TestBase
)
{
using var file = File.OpenRead(testArchive);
using var protectedStream = SharpCompressStream.Create(
#if !NETFRAMEWORK && !NETSTANDARD2_0
await using var protectedStream = SharpCompressStream.Create(
new ForwardOnlyStream(file, options.BufferSize),
leaveOpen: true,
throwOnDispose: true,
bufferSize: options.BufferSize
);
using var testStream = new TestStream(protectedStream);
await using var testStream = new TestStream(protectedStream);
#else
using var protectedStream = SharpCompressStream.Create(
new ForwardOnlyStream(file, options.BufferSize),
leaveOpen: true,
throwOnDispose: true,
bufferSize: options.BufferSize
);
using var testStream = new TestStream(protectedStream);
#endif
await using (
var reader = await ReaderFactory.OpenAsyncReader(
new AsyncOnlyStream(testStream),

View File

@@ -163,20 +163,25 @@ public class TarReaderAsyncTests : ReaderTests
}
[Fact]
public void Tar_With_TarGz_With_Flushed_EntryStream_Async()
public async ValueTask Tar_With_TarGz_With_Flushed_EntryStream_Async()
{
var archiveFullPath = Path.Combine(TEST_ARCHIVES_PATH, "Tar.ContainsTarGz.tar");
using Stream stream = File.OpenRead(archiveFullPath);
using var reader = ReaderFactory.OpenReader(stream);
Assert.True(reader.MoveToNextEntry());
await using var reader = await ReaderFactory.OpenAsyncReader(stream);
Assert.True(await reader.MoveToNextEntryAsync());
Assert.Equal("inner.tar.gz", reader.Entry.Key);
using var entryStream = reader.OpenEntryStream();
using var flushingStream = new FlushOnDisposeStream(entryStream);
#if !NETFRAMEWORK && !NETSTANDARD2_0
await using var entryStream = await reader.OpenEntryStreamAsync();
await using var flushingStream = new FlushOnDisposeStream(entryStream);
#else
using var entryStream = reader.OpenEntryStream();
using var flushingStream = new FlushOnDisposeStream(entryStream);
#endif
// Extract inner.tar.gz
using var innerReader = ReaderFactory.OpenReader(flushingStream);
Assert.True(innerReader.MoveToNextEntry());
await using var innerReader = await ReaderFactory.OpenAsyncReader(flushingStream);
Assert.True(await innerReader.MoveToNextEntryAsync());
Assert.Equal("test", innerReader.Entry.Key);
}