Tar detection works

This commit is contained in:
Adam Hathcock
2026-01-17 13:39:57 +00:00
parent 8e54b10b7f
commit 4c4b727bd7
17 changed files with 254 additions and 254 deletions

View File

@@ -6,12 +6,9 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
using SharpCompress.Common.Tar;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Writers;
using SharpCompress.Writers.Tar;
namespace SharpCompress.Archives.Tar;
@@ -176,11 +173,11 @@ public partial class TarArchive
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
var tarHeader = new TarHeader(new ArchiveEncoding());
var readSucceeded = await tarHeader.ReadAsync(stream);
var reader = new AsyncBinaryReader(stream, false);
var readSucceeded = await tarHeader.ReadAsync(reader);
var isEmptyArchive =
tarHeader.Name?.Length == 0
&& tarHeader.Size == 0

View File

@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
@@ -496,7 +497,7 @@ internal sealed class TarHeader
return true;
}
internal async Task<bool> ReadAsync(Stream stream)
internal async Task<bool> ReadAsync(AsyncBinaryReader reader)
{
string? longName = null;
string? longLinkName = null;
@@ -506,7 +507,7 @@ internal sealed class TarHeader
do
{
buffer = await ReadBlockAsync(stream);
buffer = await ReadBlockAsync(reader);
if (buffer.Length == 0)
{
@@ -519,12 +520,12 @@ internal sealed class TarHeader
// to apply to the header that follows them.
if (entryType == EntryType.LongName)
{
longName = await ReadLongNameAsync(stream, buffer);
longName = await ReadLongNameAsync(reader, buffer);
continue;
}
else if (entryType == EntryType.LongLink)
{
longLinkName = await ReadLongNameAsync(stream, buffer);
longLinkName = await ReadLongNameAsync(reader, buffer);
continue;
}
@@ -616,36 +617,27 @@ internal sealed class TarHeader
public string? Magic { get; set; }
private static async Task<byte[]> ReadBlockAsync(Stream stream)
private static async ValueTask<byte[]> ReadBlockAsync(AsyncBinaryReader reader)
{
var buffer = new byte[BLOCK_SIZE];
int bytesRead = 0;
int totalBytesRead = 0;
while (totalBytesRead < BLOCK_SIZE)
var buffer = ArrayPool<byte>.Shared.Rent(BLOCK_SIZE);
try
{
bytesRead = await stream.ReadAsync(buffer, totalBytesRead, BLOCK_SIZE - totalBytesRead);
if (bytesRead == 0)
await reader.ReadBytesAsync(buffer, 0, BLOCK_SIZE);
if (buffer.Length != 0 && buffer.Length < BLOCK_SIZE)
{
break;
throw new InvalidFormatException("Buffer is invalid size");
}
totalBytesRead += bytesRead;
}
if (totalBytesRead == 0)
return buffer;
}
finally
{
return Array.Empty<byte>();
ArrayPool<byte>.Shared.Return(buffer);
}
if (totalBytesRead < BLOCK_SIZE)
{
throw new InvalidFormatException("Buffer is invalid size");
}
return buffer;
}
private async Task<string> ReadLongNameAsync(Stream stream, byte[] buffer)
private async Task<string> ReadLongNameAsync(AsyncBinaryReader reader, byte[] buffer)
{
var size = ReadSize(buffer);
@@ -658,33 +650,31 @@ internal sealed class TarHeader
}
var nameLength = (int)size;
var nameBytes = new byte[nameLength];
int bytesRead = 0;
int totalBytesRead = 0;
while (totalBytesRead < nameLength)
var nameBytes = ArrayPool<byte>.Shared.Rent(nameLength);
try
{
bytesRead = await stream.ReadAsync(
nameBytes,
totalBytesRead,
nameLength - totalBytesRead
);
if (bytesRead == 0)
await reader.ReadBytesAsync(buffer, 0, nameLength);
var remainingBytesToRead = BLOCK_SIZE - (nameLength % BLOCK_SIZE);
// Read the rest of the block and discard the data
if (remainingBytesToRead < BLOCK_SIZE)
{
break;
var remainingBytes = ArrayPool<byte>.Shared.Rent(remainingBytesToRead);
try
{
await reader.ReadBytesAsync(remainingBytes, 0, remainingBytesToRead);
}
finally
{
ArrayPool<byte>.Shared.Return(nameBytes);
}
}
totalBytesRead += bytesRead;
return ArchiveEncoding.Decode(nameBytes, 0, nameLength).TrimNulls();
}
var remainingBytesToRead = BLOCK_SIZE - (nameLength % BLOCK_SIZE);
// Read the rest of the block and discard the data
if (remainingBytesToRead < BLOCK_SIZE)
finally
{
var paddingBuffer = new byte[remainingBytesToRead];
await stream.ReadAsync(paddingBuffer, 0, remainingBytesToRead);
ArrayPool<byte>.Shared.Return(nameBytes);
}
return ArchiveEncoding.Decode(nameBytes, 0, nameBytes.Length).TrimNulls();
}
}

View File

@@ -54,7 +54,6 @@ internal static class TarHeaderFactory
}
}
internal static async IAsyncEnumerable<TarHeader?> ReadHeaderAsync(
StreamingMode mode,
Stream stream,
@@ -66,26 +65,26 @@ internal static class TarHeaderFactory
TarHeader? header = null;
try
{
var reader = new AsyncBinaryReader(stream, false);
header = new TarHeader(archiveEncoding);
if (!await header.ReadAsync(stream))
if (!await header.ReadAsync(reader))
{
yield break;
}
switch (mode)
{
case StreamingMode.Seekable:
{
header.DataStartPosition = stream.Position;
{
header.DataStartPosition = stream.Position;
//skip to nearest 512
stream.Position += PadTo512(header.Size);
}
//skip to nearest 512
stream.Position += PadTo512(header.Size);
}
break;
case StreamingMode.Streaming:
{
header.PackedStream = new TarReadOnlySubStream(stream, header.Size);
}
{
header.PackedStream = new TarReadOnlySubStream(stream, header.Size);
}
break;
default:
{

View File

@@ -39,14 +39,14 @@ namespace SharpCompress.Factories
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
AceReader.OpenReader(stream, options);
public IAsyncReader OpenAsyncReader(
public ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)AceReader.OpenReader(stream, options);
return new((IAsyncReader)AceReader.OpenReader(stream, options));
}
}
}

View File

@@ -52,14 +52,14 @@ namespace SharpCompress.Factories
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
ArcReader.OpenReader(stream, options);
public IAsyncReader OpenAsyncReader(
public ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)ArcReader.OpenReader(stream, options);
return new((IAsyncReader)ArcReader.OpenReader(stream, options));
}
public override async ValueTask<bool> IsArchiveAsync(

View File

@@ -39,14 +39,14 @@ namespace SharpCompress.Factories
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
ArjReader.OpenReader(stream, options);
public IAsyncReader OpenAsyncReader(
public ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)ArjReader.OpenReader(stream, options);
return new((IAsyncReader)ArjReader.OpenReader(stream, options));
}
}
}

View File

@@ -144,14 +144,14 @@ public class GZipFactory
GZipReader.OpenReader(stream, options);
/// <inheritdoc/>
public IAsyncReader OpenAsyncReader(
public ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)GZipReader.OpenReader(stream, options);
return new((IAsyncReader)GZipReader.OpenReader(stream, options));
}
/// <inheritdoc/>

View File

@@ -111,14 +111,14 @@ public class RarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReade
RarReader.OpenReader(stream, options);
/// <inheritdoc/>
public IAsyncReader OpenAsyncReader(
public ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)RarReader.OpenReader(stream, options);
return new((IAsyncReader)RarReader.OpenReader(stream, options));
}
#endregion

View File

@@ -1,25 +1,15 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Archives;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Compressors;
using SharpCompress.Compressors.BZip2;
using SharpCompress.Compressors.Deflate;
using SharpCompress.Compressors.LZMA;
using SharpCompress.Compressors.Lzw;
using SharpCompress.Compressors.Xz;
using SharpCompress.Compressors.ZStandard;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.Tar;
using SharpCompress.Writers;
using SharpCompress.Writers.Tar;
using GZipArchive = SharpCompress.Archives.GZip.GZipArchive;
namespace SharpCompress.Factories;
@@ -45,7 +35,7 @@ public class TarFactory
/// <inheritdoc/>
public override IEnumerable<string> GetSupportedExtensions()
{
foreach (var testOption in compressionOptions)
foreach (var testOption in TarWrapper.Wrappers)
{
foreach (var ext in testOption.KnownExtensions)
{
@@ -59,15 +49,55 @@ public class TarFactory
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize
) => TarArchive.IsTarFile(stream);
)
{
var rewindableStream = new SharpCompressStream(stream);
long pos = rewindableStream.GetPosition();
foreach (var wrapper in TarWrapper.Wrappers)
{
rewindableStream.StackSeek(pos);
if (wrapper.IsMatch(rewindableStream))
{
rewindableStream.StackSeek(pos);
var decompressedStream = wrapper.CreateStream(rewindableStream);
if (TarArchive.IsTarFile(decompressedStream))
{
rewindableStream.StackSeek(pos);
return true;
}
}
}
return false;
}
/// <inheritdoc/>
public override ValueTask<bool> IsArchiveAsync(
public override async ValueTask<bool> IsArchiveAsync(
Stream stream,
string? password = null,
int bufferSize = ReaderOptions.DefaultBufferSize,
CancellationToken cancellationToken = default
) => TarArchive.IsTarFileAsync(stream, cancellationToken);
)
{
var rewindableStream = new SharpCompressStream(stream);
long pos = rewindableStream.GetPosition();
foreach (var wrapper in TarWrapper.Wrappers)
{
rewindableStream.StackSeek(pos);
if (await wrapper.IsMatchAsync(rewindableStream, cancellationToken))
{
rewindableStream.StackSeek(pos);
var decompressedStream = wrapper.CreateStream(rewindableStream);
if (await TarArchive.IsTarFileAsync(decompressedStream, cancellationToken))
{
rewindableStream.StackSeek(pos);
return true;
}
}
}
return false;
}
#endregion
@@ -126,161 +156,54 @@ public class TarFactory
#region IReaderFactory
protected class TestOption
{
public readonly CompressionType Type;
public readonly Func<Stream, bool> CanHandle;
public readonly bool WrapInSharpCompressStream;
public readonly Func<Stream, Stream> CreateStream;
public readonly IEnumerable<string> KnownExtensions;
public TestOption(
CompressionType Type,
Func<Stream, bool> CanHandle,
Func<Stream, Stream> CreateStream,
IEnumerable<string> KnownExtensions,
bool WrapInSharpCompressStream = true
)
{
this.Type = Type;
this.CanHandle = CanHandle;
this.WrapInSharpCompressStream = WrapInSharpCompressStream;
this.CreateStream = CreateStream;
this.KnownExtensions = KnownExtensions;
}
}
// https://en.wikipedia.org/wiki/Tar_(computing)#Suffixes_for_compressed_files
protected TestOption[] compressionOptions =
[
new(CompressionType.None, (stream) => true, (stream) => stream, ["tar"], false), // We always do a test for IsTarFile later
new(
CompressionType.BZip2,
BZip2Stream.IsBZip2,
(stream) => new BZip2Stream(stream, CompressionMode.Decompress, false),
["tar.bz2", "tb2", "tbz", "tbz2", "tz2"]
),
new(
CompressionType.GZip,
GZipArchive.IsGZipFile,
(stream) => new GZipStream(stream, CompressionMode.Decompress),
["tar.gz", "taz", "tgz"]
),
new(
CompressionType.ZStandard,
ZStandardStream.IsZStandard,
(stream) => new ZStandardStream(stream),
["tar.zst", "tar.zstd", "tzst", "tzstd"]
),
new(
CompressionType.LZip,
LZipStream.IsLZipFile,
(stream) => new LZipStream(stream, CompressionMode.Decompress),
["tar.lz"]
),
new(
CompressionType.Xz,
XZStream.IsXZStream,
(stream) => new XZStream(stream),
["tar.xz", "txz"],
false
),
new(
CompressionType.Lzw,
LzwStream.IsLzwStream,
(stream) => new LzwStream(stream),
["tar.Z", "tZ", "taZ"],
false
),
];
/// <inheritdoc/>
internal override bool TryOpenReader(
SharpCompressStream rewindableStream,
ReaderOptions options,
out IReader? reader
)
public IReader OpenReader(Stream stream, ReaderOptions? options)
{
reader = null;
long pos = ((IStreamStack)rewindableStream).GetPosition();
TestOption? testedOption = null;
if (!string.IsNullOrWhiteSpace(options.ExtensionHint))
options ??= new ReaderOptions();
var rewindableStream = new SharpCompressStream(stream);
long pos = rewindableStream.GetPosition();
foreach (var wrapper in TarWrapper.Wrappers)
{
testedOption = compressionOptions.FirstOrDefault(a =>
a.KnownExtensions.Contains(
options.ExtensionHint,
StringComparer.CurrentCultureIgnoreCase
)
);
if (testedOption != null)
rewindableStream.StackSeek(pos);
if (wrapper.IsMatch(rewindableStream))
{
reader = TryOption(rewindableStream, options, pos, testedOption);
if (reader != null)
rewindableStream.StackSeek(pos);
var decompressedStream = wrapper.CreateStream(rewindableStream);
if (TarArchive.IsTarFile(decompressedStream))
{
return true;
rewindableStream.StackSeek(pos);
return new TarReader(rewindableStream, options, wrapper.CompressionType);
}
}
}
foreach (var testOption in compressionOptions)
{
if (testedOption == testOption)
{
continue; // Already tested above
}
((IStreamStack)rewindableStream).StackSeek(pos);
reader = TryOption(rewindableStream, options, pos, testOption);
if (reader != null)
{
return true;
}
}
return false;
}
private static IReader? TryOption(
SharpCompressStream rewindableStream,
ReaderOptions options,
long pos,
TestOption testOption
)
{
if (testOption.CanHandle(rewindableStream))
{
((IStreamStack)rewindableStream).StackSeek(pos);
var inStream = rewindableStream;
if (testOption.WrapInSharpCompressStream)
{
inStream = SharpCompressStream.Create(rewindableStream, leaveOpen: true);
}
var testStream = testOption.CreateStream(rewindableStream);
if (TarArchive.IsTarFile(testStream))
{
((IStreamStack)rewindableStream).StackSeek(pos);
return new TarReader(rewindableStream, options, testOption.Type);
}
}
return null;
throw new InvalidFormatException("Not a tar file.");
}
/// <inheritdoc/>
public IReader OpenReader(Stream stream, ReaderOptions? options) =>
TarReader.OpenReader(stream, options);
/// <inheritdoc/>
public IAsyncReader OpenAsyncReader(
public async ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
options ??= new ReaderOptions();
var rewindableStream = new SharpCompressStream(stream);
long pos = rewindableStream.GetPosition();
foreach (var wrapper in TarWrapper.Wrappers)
{
rewindableStream.StackSeek(pos);
if (await wrapper.IsMatchAsync(rewindableStream, cancellationToken))
{
rewindableStream.StackSeek(pos);
var decompressedStream = wrapper.CreateStream(rewindableStream);
if (await TarArchive.IsTarFileAsync(decompressedStream, cancellationToken))
{
rewindableStream.StackSeek(pos);
return new TarReader(rewindableStream, options, wrapper.CompressionType);
}
}
}
return (IAsyncReader)TarReader.OpenReader(stream, options);
}

View File

@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Archives.GZip;
using SharpCompress.Common;
using SharpCompress.Compressors;
using SharpCompress.Compressors.BZip2;
using SharpCompress.Compressors.Deflate;
using SharpCompress.Compressors.LZMA;
using SharpCompress.Compressors.Lzw;
using SharpCompress.Compressors.Xz;
using SharpCompress.Compressors.ZStandard;
namespace SharpCompress.Factories;
public class TarWrapper(
CompressionType type,
Func<Stream, bool> canHandle,
Func<Stream, CancellationToken, ValueTask<bool>> canHandleAsync,
Func<Stream, Stream> createStream,
IEnumerable<string> knownExtensions,
bool wrapInSharpCompressStream = true
)
{
public CompressionType CompressionType { get; } = type;
public Func<Stream, bool> IsMatch { get; } = canHandle;
public Func<Stream, CancellationToken, ValueTask<bool>> IsMatchAsync { get; } = canHandleAsync;
public bool WrapInSharpCompressStream { get; } = wrapInSharpCompressStream;
public Func<Stream, Stream> CreateStream { get; } = createStream;
public IEnumerable<string> KnownExtensions { get; } = knownExtensions;
// https://en.wikipedia.org/wiki/Tar_(computing)#Suffixes_for_compressed_files
public static TarWrapper[] Wrappers { get; } =
[
new(
CompressionType.None,
(_) => true,
(_, _) => new ValueTask<bool>(true),
(stream) => stream,
["tar"],
false
), // We always do a test for IsTarFile later
new(
CompressionType.BZip2,
BZip2Stream.IsBZip2,
BZip2Stream.IsBZip2Async,
(stream) => new BZip2Stream(stream, CompressionMode.Decompress, false),
["tar.bz2", "tb2", "tbz", "tbz2", "tz2"]
),
new(
CompressionType.GZip,
GZipArchive.IsGZipFile,
GZipArchive.IsGZipFileAsync,
(stream) => new GZipStream(stream, CompressionMode.Decompress),
["tar.gz", "taz", "tgz"]
),
new(
CompressionType.ZStandard,
ZStandardStream.IsZStandard,
ZStandardStream.IsZStandardAsync,
(stream) => new ZStandardStream(stream),
["tar.zst", "tar.zstd", "tzst", "tzstd"]
),
new(
CompressionType.LZip,
LZipStream.IsLZipFile,
LZipStream.IsLZipFileAsync,
(stream) => new LZipStream(stream, CompressionMode.Decompress),
["tar.lz"]
),
new(
CompressionType.Xz,
XZStream.IsXZStream,
XZStream.IsXZStreamAsync,
(stream) => new XZStream(stream),
["tar.xz", "txz"],
false
),
new(
CompressionType.Lzw,
LzwStream.IsLzwStream,
LzwStream.IsLzwStreamAsync,
(stream) => new LzwStream(stream),
["tar.Z", "tZ", "taZ"],
false
),
];
}

View File

@@ -190,14 +190,14 @@ public class ZipFactory
ZipReader.OpenReader(stream, options);
/// <inheritdoc/>
public IAsyncReader OpenAsyncReader(
public ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken = default
)
{
cancellationToken.ThrowIfCancellationRequested();
return (IAsyncReader)ZipReader.OpenReader(stream, options);
return new((IAsyncReader)ZipReader.OpenReader(stream, options));
}
#endregion

View File

@@ -1,5 +1,6 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SharpCompress.Readers;
@@ -20,7 +21,7 @@ public interface IReaderFactory : Factories.IFactory
/// <param name="options"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
IAsyncReader OpenAsyncReader(
ValueTask<IAsyncReader> OpenAsyncReader(
Stream stream,
ReaderOptions? options,
CancellationToken cancellationToken

View File

@@ -70,7 +70,7 @@ public static class ReaderFactory
var bStream = new SharpCompressStream(stream, bufferSize: options.BufferSize);
long pos = ((IStreamStack)bStream).GetPosition();
long pos = bStream.GetPosition();
var factories = Factories.Factory.Factories.OfType<Factories.Factory>();
@@ -89,7 +89,7 @@ public static class ReaderFactory
{
return reader;
}
((IStreamStack)bStream).StackSeek(pos);
bStream.StackSeek(pos);
}
foreach (var factory in factories)
@@ -98,7 +98,7 @@ public static class ReaderFactory
{
continue; // Already tested above
}
((IStreamStack)bStream).StackSeek(pos);
bStream.StackSeek(pos);
if (factory.TryOpenReader(bStream, options, out var reader) && reader != null)
{
return reader;
@@ -120,13 +120,11 @@ public static class ReaderFactory
options ??= new ReaderOptions() { LeaveStreamOpen = false };
var bStream = new SharpCompressStream(stream, bufferSize: options.BufferSize);
long pos = bStream.GetPosition();
long pos = ((IStreamStack)bStream).GetPosition();
var factories = Factories.Factory.Factories.OfType<Factories.Factory>();
var factories = Factory.Factories.OfType<Factory>();
Factory? testedFactory = null;
if (!string.IsNullOrWhiteSpace(options.ExtensionHint))
{
testedFactory = factories.FirstOrDefault(a =>
@@ -135,7 +133,7 @@ public static class ReaderFactory
);
if (testedFactory is IReaderFactory readerFactory)
{
((IStreamStack)bStream).StackSeek(pos);
bStream.StackSeek(pos);
if (
await testedFactory.IsArchiveAsync(
bStream,
@@ -143,11 +141,11 @@ public static class ReaderFactory
)
)
{
((IStreamStack)bStream).StackSeek(pos);
return readerFactory.OpenAsyncReader(bStream, options, cancellationToken);
bStream.StackSeek(pos);
return await readerFactory.OpenAsyncReader(bStream, options, cancellationToken);
}
}
((IStreamStack)bStream).StackSeek(pos);
bStream.StackSeek(pos);
}
foreach (var factory in factories)
@@ -156,14 +154,14 @@ public static class ReaderFactory
{
continue; // Already tested above
}
((IStreamStack)bStream).StackSeek(pos);
bStream.StackSeek(pos);
if (
factory is IReaderFactory readerFactory
&& await factory.IsArchiveAsync(bStream, cancellationToken: cancellationToken)
)
{
((IStreamStack)bStream).StackSeek(pos);
return readerFactory.OpenAsyncReader(bStream, options, cancellationToken);
bStream.StackSeek(pos);
return await readerFactory.OpenAsyncReader(bStream, options, cancellationToken);
}
}

View File

@@ -1,4 +1,3 @@
using System.IO;
using System.Threading;
using SharpCompress.Common;
@@ -7,7 +6,7 @@ namespace SharpCompress.Readers.Tar;
public partial class TarReader
#if NET8_0_OR_GREATER
: IReaderOpenable
: IReaderOpenable
#endif
{
public static IAsyncReader OpenAsyncReader(

View File

@@ -58,9 +58,7 @@ public partial class TarReader : AbstractReader<TarEntry, TarVolume>
stream.NotNull(nameof(stream));
options = options ?? new ReaderOptions();
var rewindableStream = new SharpCompressStream(stream);
long pos = ((IStreamStack)rewindableStream).GetPosition();
if (GZipArchive.IsGZipFile(rewindableStream))
{
((IStreamStack)rewindableStream).StackSeek(pos);
@@ -72,7 +70,6 @@ public partial class TarReader : AbstractReader<TarEntry, TarVolume>
}
throw new InvalidFormatException("Not a tar file.");
}
((IStreamStack)rewindableStream).StackSeek(pos);
if (BZip2Stream.IsBZip2(rewindableStream))
{
@@ -85,7 +82,6 @@ public partial class TarReader : AbstractReader<TarEntry, TarVolume>
}
throw new InvalidFormatException("Not a tar file.");
}
((IStreamStack)rewindableStream).StackSeek(pos);
if (ZStandardStream.IsZStandard(rewindableStream))
{
@@ -110,7 +106,6 @@ public partial class TarReader : AbstractReader<TarEntry, TarVolume>
}
throw new InvalidFormatException("Not a tar file.");
}
((IStreamStack)rewindableStream).StackSeek(pos);
return new TarReader(rewindableStream, options, CompressionType.None);
}

View File

@@ -121,8 +121,16 @@ public abstract class ReaderTests : TestBase
where T : IFactory
{
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
var factory = await ArchiveFactory.FindFactoryAsync<T>(testArchive, cancellationToken);
(await factory.IsArchiveAsync(new FileInfo(testArchive).OpenRead(), cancellationToken: cancellationToken)).Should().BeTrue();
var factory = new TarFactory();
factory.IsArchive(new FileInfo(testArchive).OpenRead()).Should().BeTrue();
(
await factory.IsArchiveAsync(
new FileInfo(testArchive).OpenRead(),
cancellationToken: cancellationToken
)
)
.Should()
.BeTrue();
}
protected async Task ReadAsync(

View File

@@ -46,10 +46,8 @@ public class TarReaderAsyncTests : ReaderTests
public async ValueTask Tar_Z_Reader_Async() =>
await ReadAsync("Tar.tar.Z", CompressionType.Lzw);
[Fact]
public async ValueTask Tar_BZip2_Reader_Async_Assert() =>
await AssertArchiveAsync<TarFactory>("Tar.tar.bz2", default);
public async ValueTask Tar_Async_Assert() => await AssertArchiveAsync<TarFactory>("Tar.tar");
[Fact]
public async ValueTask Tar_BZip2_Reader_Async() =>