From c9a68593ea9ce2b0880e53203b640b501e212aae Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Thu, 23 Apr 2026 11:19:09 +0100 Subject: [PATCH] add writable checks --- .../StreamValidationExtensions.cs | 12 +++++++ .../Writers/GZip/GZipWriter.Factory.cs | 2 +- .../SevenZip/SevenZipWriter.Factory.cs | 2 +- .../Writers/Tar/TarWriter.Factory.cs | 2 +- src/SharpCompress/Writers/WriterFactory.cs | 4 +++ .../Writers/Zip/ZipWriter.Factory.cs | 2 +- .../SharpCompress.Test/WriterFactoryTests.cs | 34 +++++++++++++++++++ 7 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 tests/SharpCompress.Test/WriterFactoryTests.cs diff --git a/src/SharpCompress/StreamValidationExtensions.cs b/src/SharpCompress/StreamValidationExtensions.cs index 7420bcec..8c2b87db 100644 --- a/src/SharpCompress/StreamValidationExtensions.cs +++ b/src/SharpCompress/StreamValidationExtensions.cs @@ -31,6 +31,18 @@ internal static class StreamValidationExtensions return stream; } + internal static Stream RequireWritable(this Stream stream) + { + stream.NotNull(nameof(stream)); + + if (!stream.CanWrite) + { + throw new ArgumentException("Stream must be writable", nameof(stream)); + } + + return stream; + } + internal static IReadOnlyList RequireSeekable(this IReadOnlyList streams) { streams.NotNull(nameof(streams)); diff --git a/src/SharpCompress/Writers/GZip/GZipWriter.Factory.cs b/src/SharpCompress/Writers/GZip/GZipWriter.Factory.cs index f4f2b902..cae0b91d 100644 --- a/src/SharpCompress/Writers/GZip/GZipWriter.Factory.cs +++ b/src/SharpCompress/Writers/GZip/GZipWriter.Factory.cs @@ -22,7 +22,7 @@ public partial class GZipWriter : IWriterOpenable public static IWriter OpenWriter(Stream stream, GZipWriterOptions writerOptions) { - stream.NotNull(nameof(stream)); + stream.RequireWritable(); return new GZipWriter(stream, writerOptions); } diff --git a/src/SharpCompress/Writers/SevenZip/SevenZipWriter.Factory.cs b/src/SharpCompress/Writers/SevenZip/SevenZipWriter.Factory.cs index e84d7401..1191a83a 100644 --- a/src/SharpCompress/Writers/SevenZip/SevenZipWriter.Factory.cs +++ b/src/SharpCompress/Writers/SevenZip/SevenZipWriter.Factory.cs @@ -36,7 +36,7 @@ public partial class SevenZipWriter : IWriterOpenable /// public static IWriter OpenWriter(Stream stream, SevenZipWriterOptions writerOptions) { - stream.NotNull(nameof(stream)); + stream.RequireWritable(); return new SevenZipWriter(stream, writerOptions); } diff --git a/src/SharpCompress/Writers/Tar/TarWriter.Factory.cs b/src/SharpCompress/Writers/Tar/TarWriter.Factory.cs index b00ed13e..7613374b 100644 --- a/src/SharpCompress/Writers/Tar/TarWriter.Factory.cs +++ b/src/SharpCompress/Writers/Tar/TarWriter.Factory.cs @@ -22,7 +22,7 @@ public partial class TarWriter : IWriterOpenable public static IWriter OpenWriter(Stream stream, TarWriterOptions writerOptions) { - stream.NotNull(nameof(stream)); + stream.RequireWritable(); return new TarWriter(stream, writerOptions); } diff --git a/src/SharpCompress/Writers/WriterFactory.cs b/src/SharpCompress/Writers/WriterFactory.cs index 48ef1d47..847766f9 100644 --- a/src/SharpCompress/Writers/WriterFactory.cs +++ b/src/SharpCompress/Writers/WriterFactory.cs @@ -75,6 +75,8 @@ public static class WriterFactory IWriterOptions writerOptions ) { + stream.RequireWritable(); + var factory = Factories .Factory.Factories.OfType() .FirstOrDefault(item => item.KnownArchiveType == archiveType); @@ -102,6 +104,8 @@ public static class WriterFactory CancellationToken cancellationToken = default ) { + stream.RequireWritable(); + var factory = Factories .Factory.Factories.OfType() .FirstOrDefault(item => item.KnownArchiveType == archiveType); diff --git a/src/SharpCompress/Writers/Zip/ZipWriter.Factory.cs b/src/SharpCompress/Writers/Zip/ZipWriter.Factory.cs index b6667842..825de870 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriter.Factory.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriter.Factory.cs @@ -22,7 +22,7 @@ public partial class ZipWriter : IWriterOpenable public static IWriter OpenWriter(Stream stream, ZipWriterOptions writerOptions) { - stream.NotNull(nameof(stream)); + stream.RequireWritable(); return new ZipWriter(stream, writerOptions); } diff --git a/tests/SharpCompress.Test/WriterFactoryTests.cs b/tests/SharpCompress.Test/WriterFactoryTests.cs new file mode 100644 index 00000000..ca7a45a5 --- /dev/null +++ b/tests/SharpCompress.Test/WriterFactoryTests.cs @@ -0,0 +1,34 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using SharpCompress.Common; +using SharpCompress.Test.Mocks; +using SharpCompress.Writers; +using Xunit; + +namespace SharpCompress.Test; + +public class WriterFactoryTests +{ + [Fact] + public void OpenWriter_Stream_Throws_On_Unwritable_Stream() + { + using var unwritable = new TestStream(new MemoryStream(), true, false, true); + + Assert.Throws(() => + WriterFactory.OpenWriter(unwritable, ArchiveType.Zip, WriterOptions.ForZip()) + ); + } + + [Fact] + public async ValueTask OpenAsyncWriter_Stream_Throws_On_Unwritable_Stream() + { + using var unwritable = new TestStream(new MemoryStream(), true, false, true); + + await Assert.ThrowsAsync(() => + WriterFactory + .OpenAsyncWriter(unwritable, ArchiveType.Zip, WriterOptions.ForZip()) + .AsTask() + ); + } +}