diff --git a/src/SharpCompress/Archives/ArchiveFactory.cs b/src/SharpCompress/Archives/ArchiveFactory.cs index 4da070b5..ea31d457 100644 --- a/src/SharpCompress/Archives/ArchiveFactory.cs +++ b/src/SharpCompress/Archives/ArchiveFactory.cs @@ -2,12 +2,9 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Common.Options; using SharpCompress.Factories; -using SharpCompress.IO; using SharpCompress.Readers; namespace SharpCompress.Archives; diff --git a/src/SharpCompress/Archives/Tar/TarArchive.Factory.cs b/src/SharpCompress/Archives/Tar/TarArchive.Factory.cs index 1290f97a..883169eb 100644 --- a/src/SharpCompress/Archives/Tar/TarArchive.Factory.cs +++ b/src/SharpCompress/Archives/Tar/TarArchive.Factory.cs @@ -37,12 +37,9 @@ public partial class TarArchive ) { fileInfo.NotNull(nameof(fileInfo)); - return new TarArchive( - new SourceStream( - fileInfo, - i => ArchiveVolumeFactory.GetFilePart(i, fileInfo), - readerOptions ?? new ReaderOptions() { LeaveStreamOpen = false } - ) + return OpenArchive( + [fileInfo], + readerOptions ?? new ReaderOptions() { LeaveStreamOpen = false } ); } @@ -90,9 +87,7 @@ public partial class TarArchive throw new ArgumentException("Stream must be seekable", nameof(stream)); } - return new TarArchive( - new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()) - ); + return OpenArchive([stream], readerOptions); } public static IWritableAsyncArchive OpenAsyncArchive( diff --git a/src/SharpCompress/Readers/Tar/TarReader.Factory.cs b/src/SharpCompress/Readers/Tar/TarReader.Factory.cs index b8f41e60..6dc75437 100644 --- a/src/SharpCompress/Readers/Tar/TarReader.Factory.cs +++ b/src/SharpCompress/Readers/Tar/TarReader.Factory.cs @@ -1,5 +1,13 @@ using System.IO; +using SharpCompress.Archives.GZip; +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.ZStandard; +using SharpCompress.IO; namespace SharpCompress.Readers.Tar; @@ -38,4 +46,74 @@ public partial class TarReader fileInfo.NotNull(nameof(fileInfo)); return OpenReader(fileInfo.OpenRead(), readerOptions); } + + /// + /// Opens a TarReader for Non-seeking usage with a single volume + /// + /// + /// + /// + public static IReader OpenReader(Stream stream, ReaderOptions? options = null) + { + stream.NotNull(nameof(stream)); + options ??= new ReaderOptions(); + var sharpCompressStream = SharpCompressStream.Create( + stream, + bufferSize: options.RewindableBufferSize + ); + long pos = sharpCompressStream.Position; + if (GZipArchive.IsGZipFile(sharpCompressStream)) + { + sharpCompressStream.Position = pos; + var testStream = new GZipStream(sharpCompressStream, CompressionMode.Decompress); + if (TarArchive.IsTarFile(testStream)) + { + sharpCompressStream.Position = pos; + return new TarReader(sharpCompressStream, options, CompressionType.GZip); + } + throw new InvalidFormatException("Not a tar file."); + } + sharpCompressStream.Position = pos; + if (BZip2Stream.IsBZip2(sharpCompressStream)) + { + sharpCompressStream.Position = pos; + var testStream = BZip2Stream.Create( + sharpCompressStream, + CompressionMode.Decompress, + false + ); + if (TarArchive.IsTarFile(testStream)) + { + sharpCompressStream.Position = pos; + return new TarReader(sharpCompressStream, options, CompressionType.BZip2); + } + throw new InvalidFormatException("Not a tar file."); + } + sharpCompressStream.Position = pos; + if (ZStandardStream.IsZStandard(sharpCompressStream)) + { + sharpCompressStream.Position = pos; + var testStream = new ZStandardStream(sharpCompressStream); + if (TarArchive.IsTarFile(testStream)) + { + sharpCompressStream.Position = pos; + return new TarReader(sharpCompressStream, options, CompressionType.ZStandard); + } + throw new InvalidFormatException("Not a tar file."); + } + sharpCompressStream.Position = pos; + if (LZipStream.IsLZipFile(sharpCompressStream)) + { + sharpCompressStream.Position = pos; + var testStream = new LZipStream(sharpCompressStream, CompressionMode.Decompress); + if (TarArchive.IsTarFile(testStream)) + { + sharpCompressStream.Position = pos; + return new TarReader(sharpCompressStream, options, CompressionType.LZip); + } + throw new InvalidFormatException("Not a tar file."); + } + sharpCompressStream.Position = pos; + return new TarReader(sharpCompressStream, options, CompressionType.None); + } } diff --git a/src/SharpCompress/Readers/Tar/TarReader.cs b/src/SharpCompress/Readers/Tar/TarReader.cs index 2c561c15..ce7c822b 100644 --- a/src/SharpCompress/Readers/Tar/TarReader.cs +++ b/src/SharpCompress/Readers/Tar/TarReader.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using SharpCompress.Archives.GZip; -using SharpCompress.Archives.Tar; using SharpCompress.Common; using SharpCompress.Common.Tar; using SharpCompress.Compressors; @@ -45,80 +43,6 @@ public partial class TarReader : AbstractReader }; } - #region OpenReader - - /// - /// Opens a TarReader for Non-seeking usage with a single volume - /// - /// - /// - /// - public static IReader OpenReader(Stream stream, ReaderOptions? options = null) - { - stream.NotNull(nameof(stream)); - options = options ?? new ReaderOptions(); - var sharpCompressStream = SharpCompressStream.Create( - stream, - bufferSize: options.RewindableBufferSize - ); - long pos = sharpCompressStream.Position; - if (GZipArchive.IsGZipFile(sharpCompressStream)) - { - sharpCompressStream.Position = pos; - var testStream = new GZipStream(sharpCompressStream, CompressionMode.Decompress); - if (TarArchive.IsTarFile(testStream)) - { - sharpCompressStream.Position = pos; - return new TarReader(sharpCompressStream, options, CompressionType.GZip); - } - throw new InvalidFormatException("Not a tar file."); - } - sharpCompressStream.Position = pos; - if (BZip2Stream.IsBZip2(sharpCompressStream)) - { - sharpCompressStream.Position = pos; - var testStream = BZip2Stream.Create( - sharpCompressStream, - CompressionMode.Decompress, - false - ); - if (TarArchive.IsTarFile(testStream)) - { - sharpCompressStream.Position = pos; - return new TarReader(sharpCompressStream, options, CompressionType.BZip2); - } - throw new InvalidFormatException("Not a tar file."); - } - sharpCompressStream.Position = pos; - if (ZStandardStream.IsZStandard(sharpCompressStream)) - { - sharpCompressStream.Position = pos; - var testStream = new ZStandardStream(sharpCompressStream); - if (TarArchive.IsTarFile(testStream)) - { - sharpCompressStream.Position = pos; - return new TarReader(sharpCompressStream, options, CompressionType.ZStandard); - } - throw new InvalidFormatException("Not a tar file."); - } - sharpCompressStream.Position = pos; - if (LZipStream.IsLZipFile(sharpCompressStream)) - { - sharpCompressStream.Position = pos; - var testStream = new LZipStream(sharpCompressStream, CompressionMode.Decompress); - if (TarArchive.IsTarFile(testStream)) - { - sharpCompressStream.Position = pos; - return new TarReader(sharpCompressStream, options, CompressionType.LZip); - } - throw new InvalidFormatException("Not a tar file."); - } - sharpCompressStream.Position = pos; - return new TarReader(sharpCompressStream, options, CompressionType.None); - } - - #endregion OpenReader - protected override IEnumerable GetEntries(Stream stream) => TarEntry.GetEntries( StreamingMode.Streaming, @@ -127,6 +51,4 @@ public partial class TarReader : AbstractReader Options.ArchiveEncoding, Options ); - - // GetEntriesAsync moved to TarReader.Async.cs } diff --git a/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs index 39df3cc7..a27431d2 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs @@ -253,7 +253,7 @@ public class TarArchiveAsyncTests : ArchiveTests var numberOfEntries = 0; await using ( - var archiveFactory = TarArchive.OpenAsyncArchive(new AsyncOnlyStream(memoryStream)) + var archiveFactory = await ArchiveFactory.OpenAsyncArchive(new AsyncOnlyStream(memoryStream)) ) { await foreach (var entry in archiveFactory.EntriesAsync) diff --git a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs index 058806d1..4d607935 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs @@ -7,6 +7,7 @@ using SharpCompress.Archives.Tar; using SharpCompress.Common; using SharpCompress.Readers; using SharpCompress.Readers.Tar; +using SharpCompress.Test.Mocks; using SharpCompress.Writers; using SharpCompress.Writers.Tar; using Xunit; @@ -23,6 +24,26 @@ public class TarArchiveTests : ArchiveTests [Fact] public void TarArchivePathRead() => ArchiveFileRead("Tar.tar"); + [Fact] + public void TarArchiveStreamRead_Autodetect_CompressedTar() + { + using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); + using var archive = ArchiveFactory.OpenArchive(stream); + + Assert.Equal(ArchiveType.Tar, archive.Type); + Assert.NotEmpty(archive.Entries); + } + + [Fact] + public void TarArchiveStreamRead_Throws_On_NonSeekable_Stream() + { + using Stream stream = new ForwardOnlyStream( + File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")) + ); + + Assert.Throws(() => ArchiveFactory.OpenArchive(stream)); + } + [Fact] public void Tar_FileName_Exactly_100_Characters() { @@ -53,7 +74,7 @@ public class TarArchiveTests : ArchiveTests // Step 2: check if the written tar file can be read correctly var unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive); - using (var archive2 = TarArchive.OpenArchive(unmodified)) + using (var archive2 = ArchiveFactory.OpenArchive(unmodified)) { Assert.Equal(1, archive2.Entries.Count()); Assert.Contains(filename, archive2.Entries.Select(entry => entry.Key)); @@ -72,7 +93,7 @@ public class TarArchiveTests : ArchiveTests public void Tar_NonUstarArchiveWithLongNameDoesNotSkipEntriesAfterTheLongOne() { var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "very long filename.tar"); - using var archive = TarArchive.OpenArchive(unmodified); + using var archive = ArchiveFactory.OpenArchive(unmodified); Assert.Equal(5, archive.Entries.Count()); Assert.Contains("very long filename/", archive.Entries.Select(entry => entry.Key)); Assert.Contains( @@ -119,7 +140,7 @@ public class TarArchiveTests : ArchiveTests // Step 2: check if the written tar file can be read correctly var unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive); - using (var archive2 = TarArchive.OpenArchive(unmodified)) + using (var archive2 = ArchiveFactory.OpenArchive(unmodified)) { Assert.Equal(1, archive2.Entries.Count()); Assert.Contains(longFilename, archive2.Entries.Select(entry => entry.Key)); @@ -138,7 +159,7 @@ public class TarArchiveTests : ArchiveTests public void Tar_UstarArchivePathReadLongName() { var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "ustar with long names.tar"); - using var archive = TarArchive.OpenArchive(unmodified); + using var archive = ArchiveFactory.OpenArchive(unmodified); Assert.Equal(6, archive.Entries.Count()); Assert.Contains("Directory/", archive.Entries.Select(entry => entry.Key)); Assert.Contains( @@ -285,9 +306,9 @@ public class TarArchiveTests : ArchiveTests var numberOfEntries = 0; - using (var archiveFactory = TarArchive.OpenArchive(memoryStream)) + using (var archive = ArchiveFactory.OpenArchive(memoryStream)) { - foreach (var entry in archiveFactory.Entries) + foreach (var entry in archive.Entries) { ++numberOfEntries;