add seekable checks

This commit is contained in:
Adam Hathcock
2026-04-23 09:56:26 +01:00
parent d3ff2ebe0a
commit 3ed94dd462
14 changed files with 112 additions and 0 deletions

View File

@@ -99,6 +99,8 @@ public static partial class ArchiveFactory
throw new ArchiveOperationException("No streams");
}
EnsureSeekable(streamsArray);
var firstStream = streamsArray[0];
if (streamsArray.Count == 1)
{

View File

@@ -13,6 +13,22 @@ namespace SharpCompress.Archives;
public static partial class ArchiveFactory
{
internal static void EnsureSeekable(Stream stream)
{
if (stream is null || !stream.CanSeek)
{
throw new ArgumentException("Stream must be seekable", nameof(stream));
}
}
internal static void EnsureSeekable(IReadOnlyList<Stream> streams)
{
foreach (var stream in streams)
{
EnsureSeekable(stream);
}
}
public static IArchive OpenArchive(Stream stream, ReaderOptions? readerOptions = null)
{
readerOptions ??= ReaderOptions.ForExternalStream;
@@ -80,6 +96,8 @@ public static partial class ArchiveFactory
throw new ArchiveOperationException("No streams");
}
EnsureSeekable(streamsArray);
var firstStream = streamsArray[0];
if (streamsArray.Count == 1)
{

View File

@@ -77,6 +77,7 @@ public partial class GZipArchive
)
{
streams.NotNull(nameof(streams));
SharpCompress.Archives.ArchiveFactory.EnsureSeekable(streams);
var strms = streams;
return new GZipArchive(
new SourceStream(

View File

@@ -92,6 +92,7 @@ public partial class RarArchive
)
{
streams.NotNull(nameof(streams));
SharpCompress.Archives.ArchiveFactory.EnsureSeekable(streams);
var strms = streams;
return new RarArchive(
new SourceStream(

View File

@@ -72,6 +72,7 @@ public partial class SevenZipArchive
)
{
streams.NotNull(nameof(streams));
SharpCompress.Archives.ArchiveFactory.EnsureSeekable(streams);
var strms = streams;
return new SevenZipArchive(
new SourceStream(

View File

@@ -67,6 +67,7 @@ public partial class TarArchive
)
{
streams.NotNull(nameof(streams));
SharpCompress.Archives.ArchiveFactory.EnsureSeekable(streams);
var strms = streams;
var sourceStream = new SourceStream(
strms[0],
@@ -103,6 +104,10 @@ public partial class TarArchive
)
{
stream.NotNull(nameof(stream));
if (!stream.CanSeek)
{
throw new ArgumentException("Stream must be seekable", nameof(stream));
}
var sourceStream = new SourceStream(
stream,
i => null,
@@ -159,6 +164,7 @@ public partial class TarArchive
{
cancellationToken.ThrowIfCancellationRequested();
streams.NotNull(nameof(streams));
SharpCompress.Archives.ArchiveFactory.EnsureSeekable(streams);
var strms = streams;
var sourceStream = new SourceStream(
strms[0],

View File

@@ -68,6 +68,7 @@ public partial class ZipArchive
)
{
streams.NotNull(nameof(streams));
SharpCompress.Archives.ArchiveFactory.EnsureSeekable(streams);
var strms = streams;
return new ZipArchive(
new SourceStream(
@@ -132,6 +133,7 @@ public partial class ZipArchive
)
{
cancellationToken.ThrowIfCancellationRequested();
SharpCompress.Archives.ArchiveFactory.EnsureSeekable(streams);
return new((IWritableAsyncArchive<ZipWriterOptions>)OpenArchive(streams, readerOptions));
}

View File

@@ -1,9 +1,11 @@
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using SharpCompress.Archives;
using SharpCompress.Common;
using SharpCompress.Factories;
using SharpCompress.Test.Mocks;
using Xunit;
namespace SharpCompress.Test;
@@ -61,6 +63,26 @@ public class ArchiveFactoryTests : TestBase
Assert.Equal(startPosition, stream.Position);
}
[Fact]
public void OpenArchive_StreamCollection_Throws_On_NonSeekable_Stream()
{
using var nonSeekable = new ForwardOnlyStream(new MemoryStream());
using var seekable = new MemoryStream();
Assert.Throws<ArgumentException>(() => ArchiveFactory.OpenArchive([nonSeekable, seekable]));
}
[Fact]
public async ValueTask OpenAsyncArchive_StreamCollection_Throws_On_NonSeekable_Stream()
{
using var nonSeekable = new ForwardOnlyStream(new MemoryStream());
using var seekable = new MemoryStream();
await Assert.ThrowsAsync<ArgumentException>(() =>
ArchiveFactory.OpenAsyncArchive([nonSeekable, seekable]).AsTask()
);
}
[Fact]
public async ValueTask FindFactoryAsync_InvalidData_ThrowsArchiveOperationException()
{

View File

@@ -5,6 +5,7 @@ using SharpCompress.Archives;
using SharpCompress.Archives.GZip;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.Test.Mocks;
using SharpCompress.Writers.GZip;
using Xunit;
@@ -127,6 +128,15 @@ public class GZipArchiveTests : ArchiveTests
Assert.Equal(archive.Type, ArchiveType.GZip);
}
[Fact]
public void GZipArchive_StreamCollection_Throws_On_NonSeekable_Stream()
{
using var nonSeekable = new ForwardOnlyStream(new MemoryStream());
using var seekable = new MemoryStream();
Assert.Throws<ArgumentException>(() => GZipArchive.OpenArchive([nonSeekable, seekable]));
}
[Fact]
public void GZip_Archive_NonSeekableStream()
{

View File

@@ -122,6 +122,15 @@ public class RarArchiveTests : ArchiveTests
[Fact]
public void Rar_ArchiveStreamRead() => ArchiveStreamRead("Rar.rar");
[Fact]
public void RarArchive_StreamCollection_Throws_On_NonSeekable_Stream()
{
using var nonSeekable = new ForwardOnlyStream(new MemoryStream());
using var seekable = new MemoryStream();
Assert.Throws<ArgumentException>(() => RarArchive.OpenArchive([nonSeekable, seekable]));
}
[Fact]
public void Rar5_ArchiveStreamRead() => ArchiveStreamRead("Rar5.rar");

View File

@@ -7,6 +7,7 @@ using SharpCompress.Common;
using SharpCompress.Common.SevenZip;
using SharpCompress.Factories;
using SharpCompress.Readers;
using SharpCompress.Test.Mocks;
using Xunit;
namespace SharpCompress.Test.SevenZip;
@@ -25,6 +26,17 @@ public class SevenZipArchiveTests : ArchiveTests
[Fact]
public void SevenZipArchive_LZMA_PathRead() => ArchiveFileRead("7Zip.LZMA.7z");
[Fact]
public void SevenZipArchive_StreamCollection_Throws_On_NonSeekable_Stream()
{
using var nonSeekable = new ForwardOnlyStream(new MemoryStream());
using var seekable = new MemoryStream();
Assert.Throws<ArgumentException>(() =>
SevenZipArchive.OpenArchive([nonSeekable, seekable])
);
}
[Fact]
public void SevenZipArchive_LZMAAES_StreamRead() =>
ArchiveStreamRead(

View File

@@ -22,6 +22,16 @@ public class TarArchiveAsyncTests : ArchiveTests
[Fact]
public async ValueTask TarArchiveStreamRead_Async() => await ArchiveStreamReadAsync("Tar.tar");
[Fact]
public async ValueTask TarArchiveOpenAsyncStream_Throws_On_NonSeekable_Stream()
{
using var stream = new ForwardOnlyStream(new MemoryStream());
await Assert.ThrowsAsync<ArgumentException>(() =>
TarArchive.OpenAsyncArchive(stream).AsTask()
);
}
[Fact]
public async ValueTask Tar_FileName_Exactly_100_Characters_Async()
{

View File

@@ -34,6 +34,15 @@ public class TarArchiveTests : ArchiveTests
Assert.Throws<ArgumentException>(() => ArchiveFactory.OpenArchive(stream));
}
[Fact]
public void TarArchive_StreamCollection_Throws_On_NonSeekable_Stream()
{
using var nonSeekable = new ForwardOnlyStream(new MemoryStream());
using var seekable = new MemoryStream();
Assert.Throws<ArgumentException>(() => TarArchive.OpenArchive([nonSeekable, seekable]));
}
[Fact]
public void Tar_FileName_Exactly_100_Characters()
{

View File

@@ -28,6 +28,15 @@ public class ZipArchiveTests : ArchiveTests
[Fact]
public void Zip_BZip2_ArchiveStreamRead() => ArchiveStreamRead("Zip.bzip2.zip");
[Fact]
public void ZipArchive_StreamCollection_Throws_On_NonSeekable_Stream()
{
using var nonSeekable = new NonSeekableMemoryStream();
using var seekable = new MemoryStream();
Assert.Throws<ArgumentException>(() => ZipArchive.OpenArchive([nonSeekable, seekable]));
}
[Fact]
public void Zip_Deflate_Streamed2_ArchiveStreamRead() =>
ArchiveStreamRead("Zip.deflate.dd-.zip");