diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index 96f99488..c28c5f9c 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -1,5 +1,5 @@ name: SharpCompress -on: [push] +on: [push, pull_request] jobs: build: @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v1 - uses: actions/setup-dotnet@v1 with: - dotnet-version: 3.1.300 + dotnet-version: 3.1.301 - run: dotnet run -p src/build/build.csproj - uses: actions/upload-artifact@v2 with: diff --git a/README.md b/README.md index 437b894e..24b16aa5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # SharpCompress -SharpCompress is a compression library in pure C# for .NET Standard 1.3 and 2.0 that can unrar, un7zip, unzip, untar unbzip2 and ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip are implemented. +SharpCompress is a compression library in pure C# for .NET Standard 1.3 and 2.0 that can unrar, un7zip, unzip, untar unbzip2, ungzip, unlzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip/lzip are implemented. The major feature is support for non-seekable streams so large files can be processed on the fly (i.e. download stream). diff --git a/global.json b/global.json index 9ee6c25f..c120c811 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "3.1.300" + "version": "3.1.301" } } \ No newline at end of file diff --git a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs index 48c6c38a..0e9e4041 100644 --- a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs @@ -8,11 +8,6 @@ namespace SharpCompress.Archives { public static void WriteTo(this IArchiveEntry archiveEntry, Stream streamToWriteTo) { - if (archiveEntry.Archive.Type == ArchiveType.Rar && archiveEntry.Archive.IsSolid) - { - throw new InvalidFormatException("Cannot use Archive random access on SOLID Rar files."); - } - if (archiveEntry.IsDirectory) { throw new ExtractionException("Entry is a file directory and cannot be extracted."); diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs index 3f2360b2..cb4fb169 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs @@ -57,11 +57,6 @@ namespace SharpCompress.Archives.Rar public Stream OpenEntryStream() { - if (archive.IsSolid) - { - throw new InvalidOperationException("Use ExtractAllEntries to extract SOLID archives."); - } - if (IsRarV3) { return new RarStream(archive.UnpackV1.Value, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast(), archive)); diff --git a/src/SharpCompress/Common/ArchiveEncoding.cs b/src/SharpCompress/Common/ArchiveEncoding.cs index e2c0247b..1f0b6eff 100644 --- a/src/SharpCompress/Common/ArchiveEncoding.cs +++ b/src/SharpCompress/Common/ArchiveEncoding.cs @@ -27,9 +27,13 @@ namespace SharpCompress.Common public Func? CustomDecoder { get; set; } public ArchiveEncoding() + : this(Encoding.Default, Encoding.Default) { - Default = Encoding.GetEncoding(437); - Password = Encoding.GetEncoding(437); + } + public ArchiveEncoding(Encoding def, Encoding password) + { + Default = def; + Password = password; } #if NETSTANDARD1_3 || NETSTANDARD2_0 || NETSTANDARD2_1 diff --git a/src/SharpCompress/Common/Tar/TarFilePart.cs b/src/SharpCompress/Common/Tar/TarFilePart.cs index f1a56216..2e11db3d 100644 --- a/src/SharpCompress/Common/Tar/TarFilePart.cs +++ b/src/SharpCompress/Common/Tar/TarFilePart.cs @@ -24,7 +24,7 @@ namespace SharpCompress.Common.Tar if (_seekableStream != null) { _seekableStream.Position = Header.DataStartPosition!.Value; - return new ReadOnlySubStream(_seekableStream, Header.Size); + return new TarReadOnlySubStream(_seekableStream, Header.Size); } return Header.PackedStream; } diff --git a/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs b/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs index ed808d42..f0782c6e 100644 --- a/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs +++ b/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs @@ -20,22 +20,24 @@ namespace SharpCompress.Common.Tar { return; } + _isDisposed = true; + if (disposing) { - long skipBytes = _amountRead % 512; - if (skipBytes == 0) + // Ensure we read all remaining blocks for this entry. + Stream.Skip(BytesLeftToRead); + _amountRead += BytesLeftToRead; + + // If the last block wasn't a full 512 bytes, skip the remaining padding bytes. + var bytesInLastBlock = _amountRead % 512; + + if (bytesInLastBlock != 0) { - return; + Stream.Skip(512 - bytesInLastBlock); } - skipBytes = 512 - skipBytes; - if (skipBytes == 0) - { - return; - } - var buffer = new byte[skipBytes]; - Stream.ReadFully(buffer); } + base.Dispose(disposing); } diff --git a/src/build/Program.cs b/src/build/Program.cs index 4ff50041..b889a2f1 100644 --- a/src/build/Program.cs +++ b/src/build/Program.cs @@ -57,7 +57,7 @@ class Program return Glob.Files(".", d); } - foreach (var file in GetFiles("*.Tests/**/*.csproj")) + foreach (var file in GetFiles("**/*.Test.csproj")) { Run("dotnet", $"test {file} -c Release -f {framework} --no-restore --no-build"); } diff --git a/tests/SharpCompress.Test/ArchiveTests.cs b/tests/SharpCompress.Test/ArchiveTests.cs index c75aab72..cfa247e5 100644 --- a/tests/SharpCompress.Test/ArchiveTests.cs +++ b/tests/SharpCompress.Test/ArchiveTests.cs @@ -83,12 +83,6 @@ namespace SharpCompress.Test }); } } - catch (InvalidFormatException) - { - //rar SOLID test needs this - stream.ThrowOnDispose = false; - throw; - } catch (IndexOutOfRangeException) { //SevenZipArchive_BZip2_Split test needs this diff --git a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs index 05ee1bb0..71917bd9 100644 --- a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs +++ b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs @@ -203,13 +203,13 @@ namespace SharpCompress.Test.Rar [Fact] public void Rar_Solid_ArchiveStreamRead() { - Assert.Throws(() => ArchiveStreamRead("Rar.solid.rar")); + ArchiveStreamRead("Rar.solid.rar"); } [Fact] public void Rar5_Solid_ArchiveStreamRead() { - Assert.Throws(() => ArchiveStreamRead("Rar5.solid.rar")); + ArchiveStreamRead("Rar5.solid.rar"); } [Fact] @@ -232,7 +232,7 @@ namespace SharpCompress.Test.Rar "Rar.multi.part03.rar", "Rar.multi.part04.rar", "Rar.multi.part05.rar", - "Rar.multi.part06.rar"}); + "Rar.multi.part06.rar"}, false); } [Fact] @@ -243,14 +243,15 @@ namespace SharpCompress.Test.Rar "Rar5.multi.part03.rar", "Rar5.multi.part04.rar", "Rar5.multi.part05.rar", - "Rar5.multi.part06.rar"}); + "Rar5.multi.part06.rar"}, false); } - private void DoRar_Multi_ArchiveStreamRead(string[] archives) + private void DoRar_Multi_ArchiveStreamRead(string[] archives, bool isSolid) { using (var archive = RarArchive.Open(archives.Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) .Select(File.OpenRead))) { + Assert.Equal(archive.IsSolid, isSolid); foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { entry.WriteToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() @@ -260,7 +261,18 @@ namespace SharpCompress.Test.Rar }); } } - VerifyFiles(); + } + + [Fact] + public void Rar5_MultiSolid_ArchiveStreamRead() + { + DoRar_Multi_ArchiveStreamRead(new string[] { + "Rar.multi.solid.part01.rar", + "Rar.multi.solid.part02.rar", + "Rar.multi.solid.part03.rar", + "Rar.multi.solid.part04.rar", + "Rar.multi.solid.part05.rar", + "Rar.multi.solid.part06.rar"}, true); } [Fact] @@ -332,13 +344,13 @@ namespace SharpCompress.Test.Rar [Fact] public void Rar_Solid_ArchiveFileRead() { - Assert.Throws(() => ArchiveFileRead("Rar.solid.rar")); + ArchiveFileRead("Rar.solid.rar"); } [Fact] public void Rar5_Solid_ArchiveFileRead() { - Assert.Throws(() => ArchiveFileRead("Rar5.solid.rar")); + ArchiveFileRead("Rar5.solid.rar"); } [Fact] diff --git a/tests/SharpCompress.Test/SharpCompress.Test.csproj b/tests/SharpCompress.Test/SharpCompress.Test.csproj index 5de24b26..47651102 100644 --- a/tests/SharpCompress.Test/SharpCompress.Test.csproj +++ b/tests/SharpCompress.Test/SharpCompress.Test.csproj @@ -13,7 +13,7 @@ - + diff --git a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs index a03bb64e..69ae3058 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs @@ -248,5 +248,45 @@ namespace SharpCompress.Test.Tar } } } + + [Fact] + public void Tar_Read_One_At_A_Time() + { + var archiveEncoding = new ArchiveEncoding { Default = Encoding.UTF8, }; + var tarWriterOptions = new TarWriterOptions(CompressionType.None, true) { ArchiveEncoding = archiveEncoding, }; + var testBytes = Encoding.UTF8.GetBytes("This is a test."); + + using (var memoryStream = new MemoryStream()) + { + using (var tarWriter = new TarWriter(memoryStream, tarWriterOptions)) + using (var testFileStream = new MemoryStream(testBytes)) + { + tarWriter.Write("test1.txt", testFileStream); + testFileStream.Position = 0; + tarWriter.Write("test2.txt", testFileStream); + } + + memoryStream.Position = 0; + + var numberOfEntries = 0; + + using (var archiveFactory = TarArchive.Open(memoryStream)) + { + foreach (var entry in archiveFactory.Entries) + { + ++numberOfEntries; + + using (var tarEntryStream = entry.OpenEntryStream()) + using (var testFileStream = new MemoryStream()) + { + tarEntryStream.CopyTo(testFileStream); + Assert.Equal(testBytes.Length, testFileStream.Length); + } + } + } + + Assert.Equal(2, numberOfEntries); + } + } } } diff --git a/tests/TestArchives/Archives/Rar.multi.solid.part01.rar b/tests/TestArchives/Archives/Rar.multi.solid.part01.rar new file mode 100644 index 00000000..e018c9d2 Binary files /dev/null and b/tests/TestArchives/Archives/Rar.multi.solid.part01.rar differ diff --git a/tests/TestArchives/Archives/Rar.multi.solid.part02.rar b/tests/TestArchives/Archives/Rar.multi.solid.part02.rar new file mode 100644 index 00000000..461774a9 Binary files /dev/null and b/tests/TestArchives/Archives/Rar.multi.solid.part02.rar differ diff --git a/tests/TestArchives/Archives/Rar.multi.solid.part03.rar b/tests/TestArchives/Archives/Rar.multi.solid.part03.rar new file mode 100644 index 00000000..cef5a871 Binary files /dev/null and b/tests/TestArchives/Archives/Rar.multi.solid.part03.rar differ diff --git a/tests/TestArchives/Archives/Rar.multi.solid.part04.rar b/tests/TestArchives/Archives/Rar.multi.solid.part04.rar new file mode 100644 index 00000000..0f78edfe Binary files /dev/null and b/tests/TestArchives/Archives/Rar.multi.solid.part04.rar differ diff --git a/tests/TestArchives/Archives/Rar.multi.solid.part05.rar b/tests/TestArchives/Archives/Rar.multi.solid.part05.rar new file mode 100644 index 00000000..cce6e105 Binary files /dev/null and b/tests/TestArchives/Archives/Rar.multi.solid.part05.rar differ diff --git a/tests/TestArchives/Archives/Rar.multi.solid.part06.rar b/tests/TestArchives/Archives/Rar.multi.solid.part06.rar new file mode 100644 index 00000000..84283968 Binary files /dev/null and b/tests/TestArchives/Archives/Rar.multi.solid.part06.rar differ