Compare commits

...

108 Commits

Author SHA1 Message Date
Adam Hathcock
c635f00899 mark as 0.37.1 2024-04-27 09:12:17 +01:00
Adam Hathcock
1393629bc5 Mark sourcelink as PrivateAssets="All" 2024-04-27 06:15:29 +01:00
Adam Hathcock
49ce17b759 update zstdsharp.port and net8 is only trimmable 2024-04-25 08:35:52 +01:00
Adam Hathcock
74888021c8 Merge pull request #835 from Blokyk/fix-736
Prevent infinite loop when reading corrupted archive
2024-04-24 09:20:44 +01:00
Adam Hathcock
9483856439 fmt 2024-04-24 09:17:42 +01:00
blokyk
dbbc7c8132 fix(tar): prevent infinite loop when reading corrupted archive 2024-04-24 03:13:13 +02:00
Adam Hathcock
b203d165f5 Mark for 0.37.0 2024-04-23 10:25:32 +01:00
Adam Hathcock
c695e1136d Merge pull request #828 from adamhathcock/remove-netstandard20
Remove ~netstandard20~ just net7.0
2024-04-23 10:18:24 +01:00
Adam Hathcock
d847202308 add back net standard 2.0 2024-04-23 09:59:30 +01:00
Adam Hathcock
9d24e0a4b8 set package locks and central management 2024-04-23 09:37:25 +01:00
Adam Hathcock
745fe1eb9f references 2024-04-23 09:28:33 +01:00
Adam Hathcock
3e52b85e9d Merge remote-tracking branch 'origin/master' into remove-netstandard20
# Conflicts:
#	.config/dotnet-tools.json
2024-04-23 09:26:44 +01:00
Adam Hathcock
d26f020b50 Merge pull request #832 from adamhathcock/remove-ignored-nulls
Remove ignored nulls
2024-04-23 09:25:08 +01:00
Adam Hathcock
095b5f702c get rid of another null! 2024-04-23 09:20:20 +01:00
Adam Hathcock
9622853b8d fix and fmt 2024-04-23 09:16:05 +01:00
Adam Hathcock
b94e75fabe try to fix more tests 2024-04-23 09:06:49 +01:00
Adam Hathcock
23dd041e2e fix some tests 2024-04-23 08:52:10 +01:00
Adam Hathcock
c73ca21b4d fmt 2024-04-22 15:19:05 +01:00
Adam Hathcock
7ebdc85ad2 more null clean up 2024-04-22 15:17:24 +01:00
Adam Hathcock
99e2c8c90d more clean up 2024-04-22 15:10:22 +01:00
Adam Hathcock
f24bfdf945 fix tests 2024-04-22 14:57:08 +01:00
Adam Hathcock
7963233702 add missing usings 2024-04-22 14:18:41 +01:00
Adam Hathcock
b550df2038 get rid of more ! and update csharpier 2024-04-22 14:17:08 +01:00
Adam Hathcock
fb55624f5f add more null handling 2024-04-18 14:25:10 +01:00
Adam Hathcock
e96366f489 Entry can be null and remove other ! usages 2024-04-18 13:24:03 +01:00
Adam Hathcock
900190cf54 Merge pull request #829 from NeuroXiq/patch-1
Update README.md - Change API Docs to DNDocs
2024-04-15 08:14:16 +01:00
Marek Węglarz
2af744b474 Update README.md 2024-04-15 04:28:15 +02:00
Adam Hathcock
11153084e2 update csharpier 2024-04-11 15:47:39 +01:00
Adam Hathcock
4b9c814bfc remove .netstandard 2.0 and clean up 2024-04-11 15:46:43 +01:00
Adam Hathcock
1b5d3a3b6e Merge pull request #825 from adamhathcock/tar-corruption
Fix tar corruption when sizes mismatch
2024-04-11 13:11:29 +01:00
Adam Hathcock
373637e6a7 more logic fixes 2024-04-11 09:05:45 +01:00
Adam Hathcock
cb223217c1 actually, transfer block is different than overall transfer 2024-04-10 16:12:01 +01:00
Adam Hathcock
eab97a3f8b calculate remaining afterwards 2024-04-10 08:53:20 +01:00
Adam Hathcock
fdfaa8ab45 add max transfer size to tar 2024-04-09 15:35:15 +01:00
Adam Hathcock
2321d9dbee fix patch 2024-04-09 08:56:15 +01:00
Adam Hathcock
bf74dd887a Fix tar corruption when sizes mismatch 2024-04-09 08:19:23 +01:00
Adam Hathcock
3612035894 Merge pull request #823 from klimatr26/new-7z-filters
Add support for 7z ARM64 and RISCV filters
2024-04-08 09:56:07 +01:00
Adam Hathcock
6553e9b0cd formatting 2024-04-08 09:50:37 +01:00
klimatr26
09f2410170 Add support for 7z ARM64 and RISCV filters 2024-04-05 15:00:43 -05:00
Adam Hathcock
fb73d8c0a7 Merge pull request #819 from TwanVanDongen/master
Support added for TAR LZW compression (Unix 'compress' resulting in .…
2024-03-25 08:41:34 +00:00
Twan van Dongen
f2b0368078 CSharpier reformat missed 2024-03-24 16:29:29 +01:00
Twan van Dongen
02301ecf6d Support added for TAR LZW compression (Unix 'compress' resulting in .Z files) 2024-03-24 16:23:25 +01:00
Adam Hathcock
bcb61ee3e4 Merge pull request #817 from btomblinson/master
#809 Add README.md to csproj for NuGet
2024-03-18 08:34:20 +00:00
btomblinson
6a824429d0 #809 Add README.md to csproj for NuGet 2024-03-16 22:52:36 -06:00
Adam Hathcock
6a52f9097f Merge pull request #815 from adamhathcock/code-clean-up
Code clean up
2024-03-14 16:01:34 +00:00
Adam Hathcock
3fa85fc516 Merge branch 'master' into code-clean-up 2024-03-14 15:58:29 +00:00
Adam Hathcock
498d132d8a Merge pull request #816 from coderb/pullreq-rar-memusage
rar5 improve memory usage
2024-03-14 15:58:15 +00:00
root
b6340f1458 rar5 improve memory usage
use ArrayPool for stream buffer
  use stackalloc for methods on file decompression code path
2024-03-14 11:50:45 -04:00
Adam Hathcock
4afc7ae2e4 use complete namespace 2024-03-14 13:07:40 +00:00
Adam Hathcock
95975a4c33 even more clean up 2024-03-14 09:07:21 +00:00
Adam Hathcock
198a0673a2 more clean up 2024-03-14 09:00:44 +00:00
Adam Hathcock
94d1503c64 more clean up 2024-03-14 08:57:16 +00:00
Adam Hathcock
5f13e245f0 more clean up on tests 2024-03-14 08:53:08 +00:00
Adam Hathcock
2715ae645d use var 2024-03-14 08:38:12 +00:00
Adam Hathcock
0299232cb5 just using rider to clean up 2024-03-14 08:37:17 +00:00
Adam Hathcock
93e181cfd9 update csharpier 2024-03-14 08:29:30 +00:00
Adam Hathcock
8072eb1212 Merge pull request #814 from coderb/pullreq-rar5-redir
rar5 read FHEXTRA_REDIR and expose via RarEntry
2024-03-14 08:26:06 +00:00
root
226ce340f2 rar5 read FHEXTRA_REDIR and expose via RarEntry
NOTE: api user should skip entries where RarEntry.IsRedir is true and not call OpenEntryStream()
2024-03-14 04:17:31 -04:00
Adam Hathcock
ab5535eba3 Merge pull request #807 from TwanVanDongen/master
Support for decompressing Zip Shrink (Method:1)
2024-01-29 08:27:32 +00:00
Adam Hathcock
8da2499495 Merge pull request #805 from DannyBoyk/804_Fix_ZIP_Decryption
Zip: Use last modified time from basic header when validating zip decryption
2024-01-29 08:26:17 +00:00
Twan van Dongen
c057ffb153 Refrormatted using CSharpier 2024-01-27 18:59:56 +01:00
Twan van Dongen
fe13d29549 Merge branch 'master' of https://github.com/TwanVanDongen/sharpcompress 2024-01-27 18:31:13 +01:00
Twan van Dongen
225aaab4f4 Support for decompressing Zip Shrink (method:1) added 2024-01-27 18:28:46 +01:00
Daniel Nash
14c973558b Zip: Use last modified time from basic header when validating zip decryption
The last modified time used for zip decryption validation must be the
one from the basic header. If UnixTimeExtraFields are present, the
previous implementation was attempting to verify against that value
instead.
Fixed #804
2024-01-26 10:54:41 -05:00
Adam Hathcock
f515ff36b6 Mark for 0.36.0 2024-01-15 08:21:36 +00:00
Adam Hathcock
ed57cfd2f9 Merge pull request #803 from DannyBoyk/802_Add_UnixTimeExtraField_Support_Zips
Add support for the UnixTimeExtraField in Zip files
2024-01-15 08:19:29 +00:00
Daniel Nash
d69559e9c7 Add support for the UnixTimeExtraField in Zip files
Fixes #802
2024-01-12 09:34:13 -05:00
Adam Hathcock
396717efd1 Merge pull request #799 from Erior/feature/DataDescriptorStream-fix-report-size-position
Fix reporting size / position
2024-01-08 09:18:34 +00:00
Adam Hathcock
284fa24464 Merge pull request #800 from Erior/feature/Expose-file-attributes-for-rar-entries
Expose file attributes for rar
2024-01-08 09:17:53 +00:00
Adam Hathcock
0a20b9179a Merge pull request #798 from Erior/feature/Fix-crash-when-not-setting-password-for-rar5
Set Empty string for Rar5 password as default
2024-01-08 09:17:07 +00:00
Adam Hathcock
a0d5037885 Merge pull request #801 from Erior/feature/771
Issue 771, remove throw on flush for readonly streams
2024-01-08 09:13:39 +00:00
Lars Vahlenberg
4477833b1d Issue 771, remove throw on flush for readonly streams 2024-01-06 00:14:34 +01:00
Lars Vahlenberg
e0a5ed4bdb Expose file attributes 2024-01-05 09:50:58 +01:00
Lars Vahlenberg
46d4b26eba Fix testing under Linux 2024-01-05 00:35:24 +01:00
Lars Vahlenberg
f7c6edf849 Fix reporting size / position 2024-01-04 23:33:39 +01:00
Lars Vahlenberg
6c157def4b set empty string if password not set 2024-01-04 21:16:07 +01:00
Adam Hathcock
741712f89f Merge pull request #794 from Erior/feature/rar5-blake2
Feature/rar5 blake2
2024-01-03 08:34:54 +00:00
Lars Vahlenberg
4f749da628 Merge branch 'develop' into feature/rar5-blake2 2024-01-02 21:26:51 +01:00
Lars Vahlenberg
8b02795d69 CSharpier 2024-01-02 21:25:40 +01:00
Lars Vahlenberg
f8a0069a5d Calc checksum when encrypted is not working for RAR5, disable for now 2024-01-02 21:18:49 +01:00
Lars Vahlenberg
388bbe047e Blake2 Archive test OK 2024-01-02 20:46:55 +01:00
Adam Hathcock
2d4ce30e58 Merge pull request #792 from DannyBoyk/791_Correct_EOCD_ZipWriter
ZipWriter: Write correct EOCD record when more than 65,535 files
2023-12-27 08:55:02 +00:00
Daniel Nash
d4fb17cf66 ZipWriter: Write correct EOCD record when more than 65,535 files
0xFFFF will be written to the EOCD to signal to use the ZIP64
CentralDirectory record when the number of files is 65,535 or more.
Fixes #791
2023-12-22 11:26:01 -05:00
Adam Hathcock
372a2c8375 Mark for 0.35.0 2023-12-18 09:59:46 +00:00
Adam Hathcock
8f27121f21 Merge pull request #789 from adamhathcock/dotnet8
Dotnet8
2023-12-18 09:57:50 +00:00
Adam Hathcock
b986bf675f just remove readme 2023-12-18 09:31:33 +00:00
Adam Hathcock
80718a461b fix readme? 2023-12-18 09:24:00 +00:00
Adam Hathcock
2d14ecf58b add readme 2023-12-18 09:20:44 +00:00
Adam Hathcock
32aa9877c0 remove caching 2023-12-18 09:16:02 +00:00
Adam Hathcock
cee3a9c11d Revert "add lock files"
This reverts commit 30a31de45b.
2023-12-18 09:15:26 +00:00
Adam Hathcock
b78643f2d8 update upload artifact 2023-12-18 09:15:15 +00:00
Adam Hathcock
30a31de45b add lock files 2023-12-18 09:13:14 +00:00
Adam Hathcock
e4c4db534c build for dotnet 8 2023-12-18 09:09:31 +00:00
Adam Hathcock
4f7a0d3ad0 CI to dotnet 8 2023-12-18 09:08:06 +00:00
Adam Hathcock
ea3a96eead update and rerun csharpier 2023-12-18 09:04:04 +00:00
Adam Hathcock
c0e01ac132 Use dotnet 8 and update deps 2023-12-18 09:01:54 +00:00
Adam Hathcock
28ea50bca4 Merge pull request #788 from Erior/develop
RAR5 decryption support
2023-12-18 08:51:25 +00:00
Lars Vahlenberg
619e44b30f CSharpier fixes 2023-12-16 03:08:51 +01:00
Lars Vahlenberg
d678275dee Implement RAR5 decryption 2023-12-16 02:53:09 +01:00
Adam Hathcock
08eed53595 Merge pull request #787 from adamhathcock/dependabot/github_actions/actions/setup-dotnet-4
Bump actions/setup-dotnet from 3 to 4
2023-12-11 10:09:28 +00:00
dependabot[bot]
ff40f7d262 Bump actions/setup-dotnet from 3 to 4
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 3 to 4.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-11 09:35:30 +00:00
Adam Hathcock
3c1ae51dae Merge pull request #786 from Erior/feature/Issue-774
LZMA EOS marker detection
2023-12-11 08:46:20 +00:00
Adam Hathcock
8a59fc9aaf Merge pull request #785 from Erior/feature/Issue-782
Handle tar files generated with tar -H oldgnu that has large uid/gid values
2023-12-11 08:44:54 +00:00
Adam Hathcock
b7ea9dd841 Merge pull request #784 from Erior/feature/rar-comment
Dont crash on reading rar5 comment #783
2023-12-11 08:44:01 +00:00
Lars Vahlenberg
0320db6b4a LZMA EOS marker detection 2023-12-09 13:41:35 +01:00
Lars Vahlenberg
18c7f58093 Handle tar files generated with tar -H oldgnu that has large uid/gid values 2023-12-04 22:35:11 +01:00
Lars Vahlenberg
7f6f7b1436 Resharpier fix 2023-12-04 20:28:16 +01:00
Lars Vahlenberg
ca49176b97 Dont crash on reading rar5 comment 2023-12-04 20:19:11 +01:00
228 changed files with 5470 additions and 4538 deletions

View File

@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"csharpier": {
"version": "0.26.1",
"version": "0.28.1",
"commands": [
"dotnet-csharpier"
]

View File

@@ -70,7 +70,7 @@ indent_style = tab
[*.{cs,csx,cake,vb,vbx}]
# Default Severity for all .NET Code Style rules below
dotnet_analyzer_diagnostic.severity = warning
dotnet_analyzer_diagnostic.severity = silent
##########################################
# File Header (Uncomment to support file headers)
@@ -269,6 +269,8 @@ dotnet_diagnostic.CA1305.severity = suggestion
dotnet_diagnostic.CA1307.severity = suggestion
dotnet_diagnostic.CA1309.severity = suggestion
dotnet_diagnostic.CA1310.severity = error
dotnet_diagnostic.CA1507.severity = suggestion
dotnet_diagnostic.CA1513.severity = suggestion
dotnet_diagnostic.CA1707.severity = suggestion
dotnet_diagnostic.CA1708.severity = suggestion
dotnet_diagnostic.CA1711.severity = suggestion
@@ -286,6 +288,7 @@ dotnet_diagnostic.CA1834.severity = error
dotnet_diagnostic.CA1845.severity = suggestion
dotnet_diagnostic.CA1848.severity = suggestion
dotnet_diagnostic.CA1852.severity = suggestion
dotnet_diagnostic.CA1860.severity = silent
dotnet_diagnostic.CA2016.severity = suggestion
dotnet_diagnostic.CA2201.severity = error
dotnet_diagnostic.CA2206.severity = error
@@ -303,13 +306,12 @@ dotnet_diagnostic.CS1998.severity = error
dotnet_diagnostic.CS8602.severity = error
dotnet_diagnostic.CS8604.severity = error
dotnet_diagnostic.CS8618.severity = error
dotnet_diagnostic.CS0618.severity = error
dotnet_diagnostic.CS0618.severity = suggestion
dotnet_diagnostic.CS1998.severity = error
dotnet_diagnostic.CS4014.severity = error
dotnet_diagnostic.CS8600.severity = error
dotnet_diagnostic.CS8603.severity = error
dotnet_diagnostic.CS8625.severity = error
dotnet_diagnostic.CS8981.severity = suggestion
dotnet_diagnostic.BL0005.severity = suggestion
@@ -318,7 +320,7 @@ dotnet_diagnostic.MVC1000.severity = suggestion
dotnet_diagnostic.RZ10012.severity = error
dotnet_diagnostic.IDE0004.severity = error # redundant cast
dotnet_diagnostic.IDE0005.severity = error
dotnet_diagnostic.IDE0005.severity = suggestion
dotnet_diagnostic.IDE0007.severity = error # Use var
dotnet_diagnostic.IDE0011.severity = error # Use braces on if statements
dotnet_diagnostic.IDE0010.severity = silent # populate switch
@@ -329,7 +331,7 @@ dotnet_diagnostic.IDE0023.severity = suggestion # use expression body for operat
dotnet_diagnostic.IDE0024.severity = silent # expression body for operators
dotnet_diagnostic.IDE0025.severity = suggestion # use expression body for properties
dotnet_diagnostic.IDE0027.severity = suggestion # Use expression body for accessors
dotnet_diagnostic.IDE0028.severity = silent
dotnet_diagnostic.IDE0028.severity = silent # expression body for accessors
dotnet_diagnostic.IDE0032.severity = suggestion # Use auto property
dotnet_diagnostic.IDE0033.severity = error # prefer tuple name
dotnet_diagnostic.IDE0037.severity = suggestion # simplify anonymous type
@@ -337,7 +339,7 @@ dotnet_diagnostic.IDE0040.severity = error # modifiers required
dotnet_diagnostic.IDE0041.severity = error # simplify null
dotnet_diagnostic.IDE0042.severity = error # deconstruct variable
dotnet_diagnostic.IDE0044.severity = suggestion # make field only when possible
dotnet_diagnostic.IDE0047.severity = suggestion # paratemeter name
dotnet_diagnostic.IDE0047.severity = suggestion # parameter name
dotnet_diagnostic.IDE0051.severity = error # unused field
dotnet_diagnostic.IDE0052.severity = error # unused member
dotnet_diagnostic.IDE0053.severity = suggestion # lambda not needed
@@ -351,11 +353,20 @@ dotnet_diagnostic.IDE0066.severity = suggestion # switch expression
dotnet_diagnostic.IDE0072.severity = suggestion # Populate switch - forces population of all cases even when default specified
dotnet_diagnostic.IDE0078.severity = suggestion # use pattern matching
dotnet_diagnostic.IDE0090.severity = suggestion # new can be simplified
dotnet_diagnostic.IDE0130.severity = error # namespace folder structure
dotnet_diagnostic.IDE0130.severity = suggestion # namespace folder structure
dotnet_diagnostic.IDE0160.severity = silent # Use block namespaces ARE NOT required
dotnet_diagnostic.IDE0161.severity = error # Please use file namespaces
dotnet_diagnostic.IDE0200.severity = suggestion # lambda not needed
dotnet_diagnostic.IDE1006.severity = suggestion # Naming rule violation: These words cannot contain lower case characters
dotnet_diagnostic.IDE0260.severity = suggestion # Use pattern matching
dotnet_diagnostic.IDE0270.severity = suggestion # Null check simplifcation
dotnet_diagnostic.IDE0290.severity = error # Primary Constructor
dotnet_diagnostic.IDE0300.severity = suggestion # Collection
dotnet_diagnostic.IDE0305.severity = suggestion # Collection ToList
dotnet_diagnostic.NX0001.severity = error
dotnet_diagnostic.NX0002.severity = silent
dotnet_diagnostic.NX0003.severity = silent
##########################################
# Styles

View File

@@ -15,18 +15,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v3
- uses: actions/setup-dotnet@v4
with:
dotnet-version: 7.0.x
- name: NuGet Caching
uses: actions/cache@v3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('packages.lock.json', '*/packages.lock.json') }}
restore-keys: |
${{ runner.os }}-nuget-
dotnet-version: 8.0.x
- run: dotnet run --project build/build.csproj
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}-sharpcompress.nupkg
path: artifacts/*

View File

@@ -10,5 +10,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RunAnalyzersDuringLiveAnalysis>False</RunAnalyzersDuringLiveAnalysis>
<RunAnalyzersDuringBuild>False</RunAnalyzersDuringBuild>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
</Project>

18
Directory.Packages.props Normal file
View File

@@ -0,0 +1,18 @@
<Project>
<ItemGroup>
<PackageVersion Include="Bullseye" Version="5.0.0" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="Glob" Version="1.1.9" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
<PackageVersion Include="Mono.Posix.NETStandard" Version="1.0.0" />
<PackageVersion Include="SimpleExec" Version="12.0.0" />
<PackageVersion Include="System.Memory" Version="4.5.5" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageVersion Include="xunit" Version="2.7.1" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.8" />
<PackageVersion Include="xunit.SkippableFact" Version="1.4.13" />
<PackageVersion Include="ZstdSharp.Port" Version="0.8.0" />
</ItemGroup>
</Project>

9
NuGet.config Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSourceMapping>
<!-- key value for <packageSource> should match key values from <packageSources> element -->
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>

View File

@@ -1,12 +1,12 @@
# SharpCompress
SharpCompress is a compression library in pure C# for .NET Standard 2.0, 2.1, .NET Core 3.1 and .NET 5.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.
SharpCompress is a compression library in pure C# for .NET Framework 4.62, .NET Standard 2.1, .NET 6.0 and NET 8.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).
GitHub Actions Build -
[![SharpCompress](https://github.com/adamhathcock/sharpcompress/actions/workflows/dotnetcore.yml/badge.svg)](https://github.com/adamhathcock/sharpcompress/actions/workflows/dotnetcore.yml)
[![Static Badge](https://img.shields.io/badge/API%20Documentation-RobiniaDocs-43bc00?logo=readme&logoColor=white)](https://www.robiniadocs.com/d/sharpcompress/api/SharpCompress.html)
[![Static Badge](https://img.shields.io/badge/API%20Docs-DNDocs-190088?logo=readme&logoColor=white)](https://dndocs.com/d/sharpcompress/api/index.html)
## Need Help?
@@ -36,7 +36,7 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un
## TODOs (always lots)
* RAR 5 decryption support
* RAR 5 decryption crc check support
* 7Zip writing
* Zip64 (Need writing and extend Reading)
* Multi-volume Zip support.

View File

@@ -17,6 +17,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{CDB425
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
global.json = global.json
.editorconfig = .editorconfig
Directory.Packages.props = Directory.Packages.props
NuGet.config = NuGet.config
EndProjectSection
EndProject
Global

View File

@@ -79,6 +79,10 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=StaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=c873eafb_002Dd57f_002D481d_002D8c93_002D77f6863c2f88/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Static readonly fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=f9fce829_002De6f4_002D4cb2_002D80f1_002D5497c44f51df/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FCONSTANT/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FFUNCTION/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FVARIABLE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
@@ -127,6 +131,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=6af8f80e_002D9fdd_002D4223_002D8e02_002D473db916f9b2/@EntryIndexedValue">&lt;SessionState ContinuousTestingIsOn="False" ContinuousTestingMode="0" FrameworkVersion="{x:Null}" IsLocked="False" Name="All tests from Solution" PlatformMonoPreference="{x:Null}" PlatformType="{x:Null}" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
&lt;Solution /&gt;
&lt;/SessionState&gt;</s:String></wpf:ResourceDictionary>

View File

@@ -61,7 +61,7 @@ Target(
Target(
Test,
DependsOn(Build),
ForEach("net7.0", "net462"),
ForEach("net8.0", "net462"),
framework =>
{
IEnumerable<string> GetFiles(string d)

View File

@@ -6,9 +6,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Bullseye" Version="4.2.1" />
<PackageReference Include="Glob" Version="1.1.9" />
<PackageReference Include="SimpleExec" Version="11.0.0" />
<PackageReference Include="Bullseye" />
<PackageReference Include="Glob" />
<PackageReference Include="SimpleExec" />
</ItemGroup>
</Project>

25
build/packages.lock.json Normal file
View File

@@ -0,0 +1,25 @@
{
"version": 1,
"dependencies": {
"net7.0": {
"Bullseye": {
"type": "Direct",
"requested": "[5.0.0, )",
"resolved": "5.0.0",
"contentHash": "bqyt+m17ym+5aN45C5oZRAjuLDt8jKiCm/ys1XfymIXSkrTFwvI/QsbY3ucPSHDz7SF7uON7B57kXFv5H2k1ew=="
},
"Glob": {
"type": "Direct",
"requested": "[1.1.9, )",
"resolved": "1.1.9",
"contentHash": "AfK5+ECWYTP7G3AAdnU8IfVj+QpGjrh9GC2mpdcJzCvtQ4pnerAGwHsxJ9D4/RnhDUz2DSzd951O/lQjQby2Sw=="
},
"SimpleExec": {
"type": "Direct",
"requested": "[12.0.0, )",
"resolved": "12.0.0",
"contentHash": "ptxlWtxC8vM6Y6e3h9ZTxBBkOWnWrm/Sa1HT+2i1xcXY3Hx2hmKDZP5RShPf8Xr9D+ivlrXNy57ktzyH8kyt+Q=="
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"sdk": {
"version": "7.0.101",
"version": "8.0.100",
"rollForward": "latestFeature"
}
}

View File

@@ -62,7 +62,7 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl
public static int ReduceSum(Vector256<int> accumulator)
{
// Add upper lane to lower lane.
Vector128<int> vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper());
var vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper());
// Add odd to even.
vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_11_01_01));
@@ -81,7 +81,7 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int EvenReduceSum(Vector256<int> accumulator)
{
Vector128<int> vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); // add upper lane to lower lane
var vsum = Sse2.Add(accumulator.GetLower(), accumulator.GetUpper()); // add upper lane to lower lane
vsum = Sse2.Add(vsum, Sse2.Shuffle(vsum, 0b_11_10_11_10)); // add high to low
// Vector128<int>.ToScalar() isn't optimized pre-net5.0 https://github.com/dotnet/runtime/pull/37882
@@ -189,29 +189,29 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static unsafe uint CalculateSse(uint adler, ReadOnlySpan<byte> buffer)
{
uint s1 = adler & 0xFFFF;
uint s2 = (adler >> 16) & 0xFFFF;
var s1 = adler & 0xFFFF;
var s2 = (adler >> 16) & 0xFFFF;
// Process the data in blocks.
uint length = (uint)buffer.Length;
uint blocks = length / BlockSize;
var length = (uint)buffer.Length;
var blocks = length / BlockSize;
length -= blocks * BlockSize;
fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
{
fixed (byte* tapPtr = &MemoryMarshal.GetReference(Tap1Tap2))
{
byte* localBufferPtr = bufferPtr;
var localBufferPtr = bufferPtr;
// _mm_setr_epi8 on x86
Vector128<sbyte> tap1 = Sse2.LoadVector128((sbyte*)tapPtr);
Vector128<sbyte> tap2 = Sse2.LoadVector128((sbyte*)(tapPtr + 0x10));
Vector128<byte> zero = Vector128<byte>.Zero;
var tap1 = Sse2.LoadVector128((sbyte*)tapPtr);
var tap2 = Sse2.LoadVector128((sbyte*)(tapPtr + 0x10));
var zero = Vector128<byte>.Zero;
var ones = Vector128.Create((short)1);
while (blocks > 0)
{
uint n = NMAX / BlockSize; /* The NMAX constraint. */
var n = NMAX / BlockSize; /* The NMAX constraint. */
if (n > blocks)
{
n = blocks;
@@ -221,15 +221,15 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl
// Process n blocks of data. At most NMAX data bytes can be
// processed before s2 must be reduced modulo BASE.
Vector128<uint> v_ps = Vector128.CreateScalar(s1 * n);
Vector128<uint> v_s2 = Vector128.CreateScalar(s2);
Vector128<uint> v_s1 = Vector128<uint>.Zero;
var v_ps = Vector128.CreateScalar(s1 * n);
var v_s2 = Vector128.CreateScalar(s2);
var v_s1 = Vector128<uint>.Zero;
do
{
// Load 32 input bytes.
Vector128<byte> bytes1 = Sse3.LoadDquVector128(localBufferPtr);
Vector128<byte> bytes2 = Sse3.LoadDquVector128(localBufferPtr + 0x10);
var bytes1 = Sse3.LoadDquVector128(localBufferPtr);
var bytes2 = Sse3.LoadDquVector128(localBufferPtr + 0x10);
// Add previous block byte sum to v_ps.
v_ps = Sse2.Add(v_ps, v_s1);
@@ -237,11 +237,11 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl
// Horizontally add the bytes for s1, multiply-adds the
// bytes by [ 32, 31, 30, ... ] for s2.
v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes1, zero).AsUInt32());
Vector128<short> mad1 = Ssse3.MultiplyAddAdjacent(bytes1, tap1);
var mad1 = Ssse3.MultiplyAddAdjacent(bytes1, tap1);
v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad1, ones).AsUInt32());
v_s1 = Sse2.Add(v_s1, Sse2.SumAbsoluteDifferences(bytes2, zero).AsUInt32());
Vector128<short> mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2);
var mad2 = Ssse3.MultiplyAddAdjacent(bytes2, tap2);
v_s2 = Sse2.Add(v_s2, Sse2.MultiplyAddAdjacent(mad2, ones).AsUInt32());
localBufferPtr += BlockSize;
@@ -281,15 +281,15 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
public static unsafe uint CalculateAvx2(uint adler, ReadOnlySpan<byte> buffer)
{
uint s1 = adler & 0xFFFF;
uint s2 = (adler >> 16) & 0xFFFF;
uint length = (uint)buffer.Length;
var s1 = adler & 0xFFFF;
var s2 = (adler >> 16) & 0xFFFF;
var length = (uint)buffer.Length;
fixed (byte* bufferPtr = &MemoryMarshal.GetReference(buffer))
{
byte* localBufferPtr = bufferPtr;
var localBufferPtr = bufferPtr;
Vector256<byte> zero = Vector256<byte>.Zero;
var zero = Vector256<byte>.Zero;
var dot3v = Vector256.Create((short)1);
var dot2v = Vector256.Create(
32,
@@ -333,29 +333,29 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl
while (length >= 32)
{
int k = length < NMAX ? (int)length : (int)NMAX;
var k = length < NMAX ? (int)length : (int)NMAX;
k -= k % 32;
length -= (uint)k;
Vector256<uint> vs10 = vs1;
Vector256<uint> vs3 = Vector256<uint>.Zero;
var vs10 = vs1;
var vs3 = Vector256<uint>.Zero;
while (k >= 32)
{
// Load 32 input bytes.
Vector256<byte> block = Avx.LoadVector256(localBufferPtr);
var block = Avx.LoadVector256(localBufferPtr);
// Sum of abs diff, resulting in 2 x int32's
Vector256<ushort> vs1sad = Avx2.SumAbsoluteDifferences(block, zero);
var vs1sad = Avx2.SumAbsoluteDifferences(block, zero);
vs1 = Avx2.Add(vs1, vs1sad.AsUInt32());
vs3 = Avx2.Add(vs3, vs10);
// sum 32 uint8s to 16 shorts.
Vector256<short> vshortsum2 = Avx2.MultiplyAddAdjacent(block, dot2v);
var vshortsum2 = Avx2.MultiplyAddAdjacent(block, dot2v);
// sum 16 shorts to 8 uint32s.
Vector256<int> vsum2 = Avx2.MultiplyAddAdjacent(vshortsum2, dot3v);
var vsum2 = Avx2.MultiplyAddAdjacent(vshortsum2, dot3v);
vs2 = Avx2.Add(vsum2.AsUInt32(), vs2);
vs10 = vs1;
@@ -434,14 +434,14 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl
[MethodImpl(InliningOptions.HotPath | InliningOptions.ShortMethod)]
private static unsafe uint CalculateScalar(uint adler, ReadOnlySpan<byte> buffer)
{
uint s1 = adler & 0xFFFF;
uint s2 = (adler >> 16) & 0xFFFF;
var s1 = adler & 0xFFFF;
var s2 = (adler >> 16) & 0xFFFF;
uint k;
fixed (byte* bufferPtr = buffer)
{
var localBufferPtr = bufferPtr;
uint length = (uint)buffer.Length;
var length = (uint)buffer.Length;
while (length > 0)
{

View File

@@ -12,39 +12,35 @@ public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IArchiveExtra
where TEntry : IArchiveEntry
where TVolume : IVolume
{
private readonly LazyReadOnlyCollection<TVolume> lazyVolumes;
private readonly LazyReadOnlyCollection<TEntry> lazyEntries;
private readonly LazyReadOnlyCollection<TVolume> _lazyVolumes;
private readonly LazyReadOnlyCollection<TEntry> _lazyEntries;
private bool _disposed;
private readonly SourceStream? _sourceStream;
public event EventHandler<ArchiveExtractionEventArgs<IArchiveEntry>>? EntryExtractionBegin;
public event EventHandler<ArchiveExtractionEventArgs<IArchiveEntry>>? EntryExtractionEnd;
public event EventHandler<CompressedBytesReadEventArgs>? CompressedBytesRead;
public event EventHandler<FilePartExtractionBeginEventArgs>? FilePartExtractionBegin;
protected ReaderOptions ReaderOptions { get; }
private bool disposed;
protected SourceStream SrcStream;
internal AbstractArchive(ArchiveType type, SourceStream srcStream)
internal AbstractArchive(ArchiveType type, SourceStream sourceStream)
{
Type = type;
ReaderOptions = srcStream.ReaderOptions;
SrcStream = srcStream;
lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(SrcStream));
lazyEntries = new LazyReadOnlyCollection<TEntry>(LoadEntries(Volumes));
ReaderOptions = sourceStream.ReaderOptions;
_sourceStream = sourceStream;
_lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(_sourceStream));
_lazyEntries = new LazyReadOnlyCollection<TEntry>(LoadEntries(Volumes));
}
#nullable disable
internal AbstractArchive(ArchiveType type)
{
Type = type;
lazyVolumes = new LazyReadOnlyCollection<TVolume>(Enumerable.Empty<TVolume>());
lazyEntries = new LazyReadOnlyCollection<TEntry>(Enumerable.Empty<TEntry>());
ReaderOptions = new();
_lazyVolumes = new LazyReadOnlyCollection<TVolume>(Enumerable.Empty<TVolume>());
_lazyEntries = new LazyReadOnlyCollection<TEntry>(Enumerable.Empty<TEntry>());
}
#nullable enable
public ArchiveType Type { get; }
void IArchiveExtractionListener.FireEntryExtractionBegin(IArchiveEntry entry) =>
@@ -65,12 +61,12 @@ public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IArchiveExtra
/// <summary>
/// Returns an ReadOnlyCollection of all the RarArchiveEntries across the one or many parts of the RarArchive.
/// </summary>
public virtual ICollection<TEntry> Entries => lazyEntries;
public virtual ICollection<TEntry> Entries => _lazyEntries;
/// <summary>
/// Returns an ReadOnlyCollection of all the RarArchiveVolumes across the one or many parts of the RarArchive.
/// </summary>
public ICollection<TVolume> Volumes => lazyVolumes;
public ICollection<TVolume> Volumes => _lazyVolumes;
/// <summary>
/// The total size of the files compressed in the archive.
@@ -84,29 +80,29 @@ public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IArchiveExtra
public virtual long TotalUncompressSize =>
Entries.Aggregate(0L, (total, cf) => total + cf.Size);
protected abstract IEnumerable<TVolume> LoadVolumes(SourceStream srcStream);
protected abstract IEnumerable<TVolume> LoadVolumes(SourceStream sourceStream);
protected abstract IEnumerable<TEntry> LoadEntries(IEnumerable<TVolume> volumes);
IEnumerable<IArchiveEntry> IArchive.Entries => Entries.Cast<IArchiveEntry>();
IEnumerable<IVolume> IArchive.Volumes => lazyVolumes.Cast<IVolume>();
IEnumerable<IVolume> IArchive.Volumes => _lazyVolumes.Cast<IVolume>();
public virtual void Dispose()
{
if (!disposed)
if (!_disposed)
{
lazyVolumes.ForEach(v => v.Dispose());
lazyEntries.GetLoaded().Cast<Entry>().ForEach(x => x.Close());
SrcStream?.Dispose();
_lazyVolumes.ForEach(v => v.Dispose());
_lazyEntries.GetLoaded().Cast<Entry>().ForEach(x => x.Close());
_sourceStream?.Dispose();
disposed = true;
_disposed = true;
}
}
void IArchiveExtractionListener.EnsureEntriesLoaded()
{
lazyEntries.EnsureFullyLoaded();
lazyVolumes.EnsureFullyLoaded();
_lazyEntries.EnsureFullyLoaded();
_lazyVolumes.EnsureFullyLoaded();
}
void IExtractionListener.FireCompressedBytesRead(

View File

@@ -31,18 +31,18 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
}
}
private readonly List<TEntry> newEntries = new List<TEntry>();
private readonly List<TEntry> removedEntries = new List<TEntry>();
private readonly List<TEntry> newEntries = new();
private readonly List<TEntry> removedEntries = new();
private readonly List<TEntry> modifiedEntries = new List<TEntry>();
private readonly List<TEntry> modifiedEntries = new();
private bool hasModifications;
private bool pauseRebuilding;
internal AbstractWritableArchive(ArchiveType type)
: base(type) { }
internal AbstractWritableArchive(ArchiveType type, SourceStream srcStream)
: base(type, srcStream) { }
internal AbstractWritableArchive(ArchiveType type, SourceStream sourceStream)
: base(type, sourceStream) { }
public override ICollection<TEntry> Entries
{
@@ -120,6 +120,10 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
{
foreach (var path in Entries.Select(x => x.Key))
{
if (path is null)
{
continue;
}
var p = path.Replace('/', '\\');
if (p.Length > 0 && p[0] == '\\')
{

View File

@@ -26,8 +26,7 @@ public static class ArchiveFactory
public static IWritableArchive Create(ArchiveType type)
{
var factory = Factory
.Factories
.OfType<IWriteableArchiveFactory>()
.Factories.OfType<IWriteableArchiveFactory>()
.FirstOrDefault(item => item.KnownArchiveType == type);
if (factory != null)

View File

@@ -11,7 +11,7 @@ internal abstract class ArchiveVolumeFactory
FileInfo? item = null;
//split 001, 002 ...
Match m = Regex.Match(part1.Name, @"^(.*\.)([0-9]+)$", RegexOptions.IgnoreCase);
var m = Regex.Match(part1.Name, @"^(.*\.)([0-9]+)$", RegexOptions.IgnoreCase);
if (m.Success)
item = new FileInfo(
Path.Combine(

View File

@@ -90,25 +90,23 @@ public class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
{
stream.CheckNotNull(nameof(stream));
return new GZipArchive(
new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions())
new SourceStream(stream, _ => null, readerOptions ?? new ReaderOptions())
);
}
public static GZipArchive Create() => new GZipArchive();
public static GZipArchive Create() => new();
/// <summary>
/// Constructor with a SourceStream able to handle FileInfo and Streams.
/// </summary>
/// <param name="srcStream"></param>
/// <param name="options"></param>
internal GZipArchive(SourceStream srcStream)
: base(ArchiveType.Tar, srcStream) { }
/// <param name="sourceStream"></param>
private GZipArchive(SourceStream sourceStream)
: base(ArchiveType.Tar, sourceStream) { }
protected override IEnumerable<GZipVolume> LoadVolumes(SourceStream srcStream)
protected override IEnumerable<GZipVolume> LoadVolumes(SourceStream sourceStream)
{
srcStream.LoadAllParts();
var idx = 0;
return srcStream.Streams.Select(a => new GZipVolume(a, ReaderOptions, idx++));
sourceStream.LoadAllParts();
return sourceStream.Streams.Select(a => new GZipVolume(a, ReaderOptions, 0));
}
public static bool IsGZipFile(string filePath) => IsGZipFile(new FileInfo(filePath));
@@ -184,7 +182,11 @@ public class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
{
using var entryStream = entry.OpenEntryStream();
writer.Write(entry.Key, entryStream, entry.LastModifiedTime);
writer.Write(
entry.Key.NotNull("Entry Key is null"),
entryStream,
entry.LastModifiedTime
);
}
}

View File

@@ -6,7 +6,7 @@ namespace SharpCompress.Archives.GZip;
public class GZipArchiveEntry : GZipEntry, IArchiveEntry
{
internal GZipArchiveEntry(GZipArchive archive, GZipFilePart part)
internal GZipArchiveEntry(GZipArchive archive, GZipFilePart? part)
: base(part) => Archive = archive;
public virtual Stream OpenEntryStream()

View File

@@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.IO;
@@ -32,7 +30,7 @@ internal sealed class GZipWritableArchiveEntry : GZipArchiveEntry, IWritableArch
public override long Crc => 0;
public override string Key { get; }
public override string? Key { get; }
public override long CompressedSize => 0;

View File

@@ -17,15 +17,11 @@ public static class IArchiveEntryExtensions
streamListener.EnsureEntriesLoaded();
streamListener.FireEntryExtractionBegin(archiveEntry);
streamListener.FireFilePartExtractionBegin(
archiveEntry.Key,
archiveEntry.Key ?? "Key",
archiveEntry.Size,
archiveEntry.CompressedSize
);
var entryStream = archiveEntry.OpenEntryStream();
if (entryStream is null)
{
return;
}
using (entryStream)
{
using Stream s = new ListeningStream(streamListener, entryStream);

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using SharpCompress.Common;
namespace SharpCompress.Archives;
@@ -59,7 +58,7 @@ public static class IArchiveExtensions
}
// Create each directory
var path = Path.Combine(destination, entry.Key);
var path = Path.Combine(destination, entry.Key.NotNull("Entry Key is null"));
if (Path.GetDirectoryName(path) is { } directory && seenDirectories.Add(path))
{
Directory.CreateDirectory(directory);

View File

@@ -13,7 +13,7 @@ namespace SharpCompress.Archives.Rar;
/// </summary>
internal class FileInfoRarArchiveVolume : RarVolume
{
internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options, int index = 0)
internal FileInfoRarArchiveVolume(FileInfo fileInfo, ReaderOptions options, int index)
: base(StreamingMode.Seekable, fileInfo.OpenRead(), FixOptions(options), index)
{
FileInfo = fileInfo;

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -14,40 +15,39 @@ namespace SharpCompress.Archives.Rar;
public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
{
internal Lazy<IRarUnpack> UnpackV2017 { get; } =
new Lazy<IRarUnpack>(() => new Compressors.Rar.UnpackV2017.Unpack());
internal Lazy<IRarUnpack> UnpackV1 { get; } =
new Lazy<IRarUnpack>(() => new Compressors.Rar.UnpackV1.Unpack());
new(() => new Compressors.Rar.UnpackV2017.Unpack());
internal Lazy<IRarUnpack> UnpackV1 { get; } = new(() => new Compressors.Rar.UnpackV1.Unpack());
/// <summary>
/// Constructor with a SourceStream able to handle FileInfo and Streams.
/// </summary>
/// <param name="srcStream"></param>
/// <param name="options"></param>
internal RarArchive(SourceStream srcStream)
: base(ArchiveType.Rar, srcStream) { }
/// <param name="sourceStream"></param>
private RarArchive(SourceStream sourceStream)
: base(ArchiveType.Rar, sourceStream) { }
protected override IEnumerable<RarArchiveEntry> LoadEntries(IEnumerable<RarVolume> volumes) =>
RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions);
protected override IEnumerable<RarVolume> LoadVolumes(SourceStream srcStream)
protected override IEnumerable<RarVolume> LoadVolumes(SourceStream sourceStream)
{
SrcStream.LoadAllParts(); //request all streams
var streams = SrcStream.Streams.ToArray();
var idx = 0;
sourceStream.LoadAllParts(); //request all streams
var streams = sourceStream.Streams.ToArray();
var i = 0;
if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions)) //test part 2 - true = multipart not split
{
SrcStream.IsVolumes = true;
sourceStream.IsVolumes = true;
streams[1].Position = 0;
SrcStream.Position = 0;
sourceStream.Position = 0;
return srcStream
.Streams
.Select(a => new StreamRarArchiveVolume(a, ReaderOptions, idx++));
}
else //split mode or single file
{
return new StreamRarArchiveVolume(SrcStream, ReaderOptions, idx++).AsEnumerable();
return sourceStream.Streams.Select(a => new StreamRarArchiveVolume(
a,
ReaderOptions,
i++
));
}
//split mode or single file
return new StreamRarArchiveVolume(sourceStream, ReaderOptions, i++).AsEnumerable();
}
protected override IReader CreateReaderForSolidExtraction()
@@ -106,7 +106,7 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
public static RarArchive Open(Stream stream, ReaderOptions? options = null)
{
stream.CheckNotNull(nameof(stream));
return new RarArchive(new SourceStream(stream, i => null, options ?? new ReaderOptions()));
return new RarArchive(new SourceStream(stream, _ => null, options ?? new ReaderOptions()));
}
/// <summary>

View File

@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -40,7 +41,10 @@ public class RarArchiveEntry : RarEntry, IArchiveEntry
get
{
CheckIncomplete();
return parts.Select(fp => fp.FileHeader).Single(fh => !fh.IsSplitAfter).FileCrc;
return BitConverter.ToUInt32(
parts.Select(fp => fp.FileHeader).Single(fh => !fh.IsSplitAfter).FileCrc,
0
);
}
}

View File

@@ -11,7 +11,7 @@ internal static class RarArchiveVolumeFactory
FileInfo? item = null;
//new style rar - ..part1 | /part01 | part001 ....
Match m = Regex.Match(part1.Name, @"^(.*\.part)([0-9]+)(\.rar)$", RegexOptions.IgnoreCase);
var m = Regex.Match(part1.Name, @"^(.*\.part)([0-9]+)(\.rar)$", RegexOptions.IgnoreCase);
if (m.Success)
item = new FileInfo(
Path.Combine(

View File

@@ -6,8 +6,8 @@ namespace SharpCompress.Archives.Rar;
internal class SeekableFilePart : RarFilePart
{
private readonly Stream stream;
private readonly string? password;
private readonly Stream _stream;
private readonly string? _password;
internal SeekableFilePart(
MarkHeader mh,
@@ -18,18 +18,27 @@ internal class SeekableFilePart : RarFilePart
)
: base(mh, fh, index)
{
this.stream = stream;
this.password = password;
_stream = stream;
_password = password;
}
internal override Stream GetCompressedStream()
{
stream.Position = FileHeader.DataStartPosition;
_stream.Position = FileHeader.DataStartPosition;
if (FileHeader.R4Salt != null)
{
return new RarCryptoWrapper(stream, password!, FileHeader.R4Salt);
var cryptKey = new CryptKey3(_password!);
return new RarCryptoWrapper(_stream, FileHeader.R4Salt, cryptKey);
}
return stream;
if (FileHeader.Rar5CryptoInfo != null)
{
var cryptKey = new CryptKey5(_password!, FileHeader.Rar5CryptoInfo);
return new RarCryptoWrapper(_stream, FileHeader.Rar5CryptoInfo.Salt, cryptKey);
}
return _stream;
}
internal override string FilePartName => "Unknown Stream - File Entry: " + FileHeader.FileName;

View File

@@ -9,7 +9,7 @@ namespace SharpCompress.Archives.Rar;
internal class StreamRarArchiveVolume : RarVolume
{
internal StreamRarArchiveVolume(Stream stream, ReaderOptions options, int index = 0)
internal StreamRarArchiveVolume(Stream stream, ReaderOptions options, int index)
: base(StreamingMode.Seekable, stream, options, index) { }
internal override IEnumerable<RarFilePart> ReadFileParts() => GetVolumeFileParts();

View File

@@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.IO;
@@ -14,14 +12,14 @@ namespace SharpCompress.Archives.SevenZip;
public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVolume>
{
private ArchiveDatabase database;
private ArchiveDatabase? _database;
/// <summary>
/// Constructor expects a filepath to an existing file.
/// </summary>
/// <param name="filePath"></param>
/// <param name="readerOptions"></param>
public static SevenZipArchive Open(string filePath, ReaderOptions readerOptions = null)
public static SevenZipArchive Open(string filePath, ReaderOptions? readerOptions = null)
{
filePath.CheckNotNullOrEmpty("filePath");
return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions());
@@ -32,7 +30,7 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
/// </summary>
/// <param name="fileInfo"></param>
/// <param name="readerOptions"></param>
public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null)
public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
fileInfo.CheckNotNull("fileInfo");
return new SevenZipArchive(
@@ -51,7 +49,7 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
/// <param name="readerOptions"></param>
public static SevenZipArchive Open(
IEnumerable<FileInfo> fileInfos,
ReaderOptions readerOptions = null
ReaderOptions? readerOptions = null
)
{
fileInfos.CheckNotNull(nameof(fileInfos));
@@ -72,7 +70,7 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
/// <param name="readerOptions"></param>
public static SevenZipArchive Open(
IEnumerable<Stream> streams,
ReaderOptions readerOptions = null
ReaderOptions? readerOptions = null
)
{
streams.CheckNotNull(nameof(streams));
@@ -91,27 +89,25 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
/// </summary>
/// <param name="stream"></param>
/// <param name="readerOptions"></param>
public static SevenZipArchive Open(Stream stream, ReaderOptions readerOptions = null)
public static SevenZipArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
stream.CheckNotNull("stream");
return new SevenZipArchive(
new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions())
new SourceStream(stream, _ => null, readerOptions ?? new ReaderOptions())
);
}
/// <summary>
/// Constructor with a SourceStream able to handle FileInfo and Streams.
/// </summary>
/// <param name="srcStream"></param>
/// <param name="options"></param>
internal SevenZipArchive(SourceStream srcStream)
: base(ArchiveType.SevenZip, srcStream) { }
/// <param name="sourceStream"></param>
private SevenZipArchive(SourceStream sourceStream)
: base(ArchiveType.SevenZip, sourceStream) { }
protected override IEnumerable<SevenZipVolume> LoadVolumes(SourceStream srcStream)
protected override IEnumerable<SevenZipVolume> LoadVolumes(SourceStream sourceStream)
{
SrcStream.LoadAllParts(); //request all streams
var idx = 0;
return new SevenZipVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported
sourceStream.NotNull("SourceStream is null").LoadAllParts(); //request all streams
return new SevenZipVolume(sourceStream, ReaderOptions, 0).AsEnumerable(); //simple single volume or split, multivolume not supported
}
public static bool IsSevenZipFile(string filePath) => IsSevenZipFile(new FileInfo(filePath));
@@ -135,13 +131,17 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
{
var stream = volumes.Single().Stream;
LoadFactory(stream);
var entries = new SevenZipArchiveEntry[database._files.Count];
for (var i = 0; i < database._files.Count; i++)
if (_database is null)
{
var file = database._files[i];
return Enumerable.Empty<SevenZipArchiveEntry>();
}
var entries = new SevenZipArchiveEntry[_database._files.Count];
for (var i = 0; i < _database._files.Count; i++)
{
var file = _database._files[i];
entries[i] = new SevenZipArchiveEntry(
this,
new SevenZipFilePart(stream, database, i, file, ReaderOptions.ArchiveEncoding)
new SevenZipFilePart(stream, _database, i, file, ReaderOptions.ArchiveEncoding)
);
}
foreach (var group in entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder))
@@ -159,12 +159,12 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
private void LoadFactory(Stream stream)
{
if (database is null)
if (_database is null)
{
stream.Position = 0;
var reader = new ArchiveReader();
reader.Open(stream);
database = reader.ReadDatabase(new PasswordProvider(ReaderOptions.Password));
_database = reader.ReadDatabase(new PasswordProvider(ReaderOptions.Password));
}
}
@@ -180,14 +180,14 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
}
}
private static ReadOnlySpan<byte> SIGNATURE =>
private static ReadOnlySpan<byte> Signature =>
new byte[] { (byte)'7', (byte)'z', 0xBC, 0xAF, 0x27, 0x1C };
private static bool SignatureMatch(Stream stream)
{
var reader = new BinaryReader(stream);
ReadOnlySpan<byte> signatureBytes = reader.ReadBytes(6);
return signatureBytes.SequenceEqual(SIGNATURE);
return signatureBytes.SequenceEqual(Signature);
}
protected override IReader CreateReaderForSolidExtraction() =>
@@ -196,30 +196,24 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
public override bool IsSolid =>
Entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder).Count() > 1;
public override long TotalSize
{
get
{
var i = Entries.Count;
return database._packSizes.Aggregate(0L, (total, packSize) => total + packSize);
}
}
public override long TotalSize =>
_database?._packSizes.Aggregate(0L, (total, packSize) => total + packSize) ?? 0;
private sealed class SevenZipReader : AbstractReader<SevenZipEntry, SevenZipVolume>
{
private readonly SevenZipArchive archive;
private CFolder currentFolder;
private Stream currentStream;
private CFileItem currentItem;
private readonly SevenZipArchive _archive;
private CFolder? _currentFolder;
private Stream? _currentStream;
private CFileItem? _currentItem;
internal SevenZipReader(ReaderOptions readerOptions, SevenZipArchive archive)
: base(readerOptions, ArchiveType.SevenZip) => this.archive = archive;
: base(readerOptions, ArchiveType.SevenZip) => this._archive = archive;
public override SevenZipVolume Volume => archive.Volumes.Single();
public override SevenZipVolume Volume => _archive.Volumes.Single();
protected override IEnumerable<SevenZipEntry> GetEntries(Stream stream)
{
var entries = archive.Entries.ToList();
var entries = _archive.Entries.ToList();
stream.Position = 0;
foreach (var dir in entries.Where(x => x.IsDirectory))
{
@@ -229,39 +223,42 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
var group in entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder)
)
{
currentFolder = group.Key;
_currentFolder = group.Key;
if (group.Key is null)
{
currentStream = Stream.Null;
_currentStream = Stream.Null;
}
else
{
currentStream = archive
.database
.GetFolderStream(
stream,
currentFolder,
new PasswordProvider(Options.Password)
);
_currentStream = _archive._database?.GetFolderStream(
stream,
_currentFolder,
new PasswordProvider(Options.Password)
);
}
foreach (var entry in group)
{
currentItem = entry.FilePart.Header;
_currentItem = entry.FilePart.Header;
yield return entry;
}
}
}
protected override EntryStream GetEntryStream() =>
CreateEntryStream(new ReadOnlySubStream(currentStream, currentItem.Size));
CreateEntryStream(
new ReadOnlySubStream(
_currentStream.NotNull("currentStream is not null"),
_currentItem?.Size ?? 0
)
);
}
private class PasswordProvider : IPasswordProvider
{
private readonly string _password;
private readonly string? _password;
public PasswordProvider(string password) => _password = password;
public PasswordProvider(string? password) => _password = password;
public string CryptoGetTextPassword() => _password;
public string? CryptoGetTextPassword() => _password;
}
}

View File

@@ -114,7 +114,7 @@ public class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
var tarHeader = new TarHeader(new ArchiveEncoding());
var readSucceeded = tarHeader.Read(new BinaryReader(stream));
var isEmptyArchive =
tarHeader.Name.Length == 0
tarHeader.Name?.Length == 0
&& tarHeader.Size == 0
&& Enum.IsDefined(typeof(EntryType), tarHeader.EntryType);
return readSucceeded || isEmptyArchive;
@@ -123,22 +123,20 @@ public class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
return false;
}
protected override IEnumerable<TarVolume> LoadVolumes(SourceStream srcStream)
protected override IEnumerable<TarVolume> LoadVolumes(SourceStream sourceStream)
{
SrcStream.LoadAllParts(); //request all streams
var idx = 0;
return new TarVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported
sourceStream.NotNull("SourceStream is null").LoadAllParts(); //request all streams
return new TarVolume(sourceStream, ReaderOptions, 1).AsEnumerable(); //simple single volume or split, multivolume not supported
}
/// <summary>
/// Constructor with a SourceStream able to handle FileInfo and Streams.
/// </summary>
/// <param name="srcStream"></param>
/// <param name="options"></param>
internal TarArchive(SourceStream srcStream)
: base(ArchiveType.Tar, srcStream) { }
/// <param name="sourceStream"></param>
private TarArchive(SourceStream sourceStream)
: base(ArchiveType.Tar, sourceStream) { }
internal TarArchive()
private TarArchive()
: base(ArchiveType.Tar) { }
protected override IEnumerable<TarArchiveEntry> LoadEntries(IEnumerable<TarVolume> volumes)
@@ -192,10 +190,14 @@ public class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
);
}
}
else
{
throw new IncompleteArchiveException("Failed to read TAR header");
}
}
}
public static TarArchive Create() => new TarArchive();
public static TarArchive Create() => new();
protected override TarArchiveEntry CreateEntryInternal(
string filePath,
@@ -225,7 +227,12 @@ public class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
{
using var entryStream = entry.OpenEntryStream();
writer.Write(entry.Key, entryStream, entry.LastModifiedTime, entry.Size);
writer.Write(
entry.Key.NotNull("Entry Key is null"),
entryStream,
entry.LastModifiedTime,
entry.Size
);
}
}

View File

@@ -7,7 +7,7 @@ namespace SharpCompress.Archives.Tar;
public class TarArchiveEntry : TarEntry, IArchiveEntry
{
internal TarArchiveEntry(TarArchive archive, TarFilePart part, CompressionType compressionType)
internal TarArchiveEntry(TarArchive archive, TarFilePart? part, CompressionType compressionType)
: base(part, compressionType) => Archive = archive;
public virtual Stream OpenEntryStream() => Parts.Single().GetCompressedStream();

View File

@@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.IO;

View File

@@ -16,10 +16,7 @@ namespace SharpCompress.Archives.Zip;
public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
{
#nullable disable
private readonly SeekableZipHeaderFactory headerFactory;
#nullable enable
private readonly SeekableZipHeaderFactory? headerFactory;
/// <summary>
/// Gets or sets the compression level applied to files added to the archive,
@@ -30,13 +27,13 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
/// <summary>
/// Constructor with a SourceStream able to handle FileInfo and Streams.
/// </summary>
/// <param name="srcStream"></param>
/// <param name="sourceStream"></param>
/// <param name="options"></param>
internal ZipArchive(SourceStream srcStream)
: base(ArchiveType.Zip, srcStream) =>
internal ZipArchive(SourceStream sourceStream)
: base(ArchiveType.Zip, sourceStream) =>
headerFactory = new SeekableZipHeaderFactory(
srcStream.ReaderOptions.Password,
srcStream.ReaderOptions.ArchiveEncoding
sourceStream.ReaderOptions.Password,
sourceStream.ReaderOptions.ArchiveEncoding
);
/// <summary>
@@ -189,21 +186,21 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
}
}
protected override IEnumerable<ZipVolume> LoadVolumes(SourceStream srcStream)
protected override IEnumerable<ZipVolume> LoadVolumes(SourceStream stream)
{
SrcStream.LoadAllParts(); //request all streams
SrcStream.Position = 0;
stream.LoadAllParts(); //request all streams
stream.Position = 0;
var streams = SrcStream.Streams.ToList();
var streams = stream.Streams.ToList();
var idx = 0;
if (streams.Count > 1) //test part 2 - true = multipart not split
if (streams.Count() > 1) //test part 2 - true = multipart not split
{
streams[1].Position += 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception
var isZip = IsZipFile(streams[1], ReaderOptions.Password);
streams[1].Position -= 4;
if (isZip)
{
SrcStream.IsVolumes = true;
stream.IsVolumes = true;
var tmp = streams[0]; //arcs as zip, z01 ... swap the zip the end
streams.RemoveAt(0);
@@ -215,7 +212,7 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
}
//split mode or single file
return new ZipVolume(SrcStream, ReaderOptions, idx++).AsEnumerable();
return new ZipVolume(stream, ReaderOptions, idx++).AsEnumerable();
}
internal ZipArchive()
@@ -224,14 +221,13 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
protected override IEnumerable<ZipArchiveEntry> LoadEntries(IEnumerable<ZipVolume> volumes)
{
var vols = volumes.ToArray();
foreach (var h in headerFactory.ReadSeekableHeader(vols.Last().Stream))
foreach (var h in headerFactory.NotNull().ReadSeekableHeader(vols.Last().Stream))
{
if (h != null)
{
switch (h.ZipHeaderType)
{
case ZipHeaderType.DirectoryEntry:
{
var deh = (DirectoryEntryHeader)h;
Stream s;
@@ -254,14 +250,14 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
yield return new ZipArchiveEntry(
this,
new SeekableZipFilePart(headerFactory, deh, s)
new SeekableZipFilePart(headerFactory.NotNull(), deh, s)
);
}
break;
case ZipHeaderType.DirectoryEnd:
{
var bytes = ((DirectoryEndHeader)h).Comment ?? Array.Empty<byte>();
volumes.Last().Comment = ReaderOptions.ArchiveEncoding.Decode(bytes);
vols.Last().Comment = ReaderOptions.ArchiveEncoding.Decode(bytes);
yield break;
}
}
@@ -282,7 +278,11 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
{
using var entryStream = entry.OpenEntryStream();
writer.Write(entry.Key, entryStream, entry.LastModifiedTime);
writer.Write(
entry.Key.NotNull("Entry Key is null"),
entryStream,
entry.LastModifiedTime
);
}
}
@@ -294,7 +294,7 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
bool closeStream
) => new ZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream);
public static ZipArchive Create() => new ZipArchive();
public static ZipArchive Create() => new();
protected override IReader CreateReaderForSolidExtraction()
{

View File

@@ -12,7 +12,7 @@ internal static class ZipArchiveVolumeFactory
//load files with zip/zipx first. Swapped to end once loaded in ZipArchive
//new style .zip, z01.. | .zipx, zx01 - if the numbers go beyond 99 then they use 100 ...1000 etc
Match m = Regex.Match(part1.Name, @"^(.*\.)(zipx?|zx?[0-9]+)$", RegexOptions.IgnoreCase);
var m = Regex.Match(part1.Name, @"^(.*\.)(zipx?|zx?[0-9]+)$", RegexOptions.IgnoreCase);
if (m.Success)
item = new FileInfo(
Path.Combine(

View File

@@ -0,0 +1,33 @@
using System.Buffers;
namespace SharpCompress;
internal static class BufferPool
{
/// <summary>
/// gets a buffer from the pool
/// </summary>
/// <param name="bufferSize">size of the buffer</param>
/// <returns>the buffer</returns>
public static byte[] Rent(int bufferSize)
{
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
return ArrayPool<byte>.Shared.Rent(bufferSize);
#else
return new byte[bufferSize];
#endif
}
/// <summary>
/// returns a buffer to the pool
/// </summary>
/// <param name="buffer">the buffer to return</param>
public static void Return(byte[] buffer)
{
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
ArrayPool<byte>.Shared.Return(buffer);
#else
// no-op
#endif
}
}

View File

@@ -8,12 +8,12 @@ public class ArchiveEncoding
/// <summary>
/// Default encoding to use when archive format doesn't specify one.
/// </summary>
public Encoding Default { get; set; }
public Encoding? Default { get; set; }
/// <summary>
/// ArchiveEncoding used by encryption schemes which don't comply with RFC 2898.
/// </summary>
public Encoding Password { get; set; }
public Encoding? Password { get; set; }
/// <summary>
/// Set this encoding when you want to force it for all encoding operations.
@@ -50,6 +50,8 @@ public class ArchiveEncoding
public Encoding GetEncoding() => Forced ?? Default ?? Encoding.UTF8;
public Encoding GetPasswordEncoding() => Password ?? Encoding.UTF8;
public Func<byte[], int, int, string> GetDecoder() =>
CustomDecoder ?? ((bytes, index, count) => GetEncoding().GetString(bytes, index, count));
}

View File

@@ -14,5 +14,7 @@ public enum CompressionType
LZip,
Xz,
Unknown,
Deflate64
Deflate64,
Shrink,
Lzw
}

View File

@@ -14,7 +14,7 @@ public abstract class Entry : IEntry
/// <summary>
/// The string key of the file internal to the Archive.
/// </summary>
public abstract string Key { get; }
public abstract string? Key { get; }
/// <summary>
/// The target of a symlink entry internal to the Archive. Will be null if not a symlink.
@@ -71,11 +71,11 @@ public abstract class Entry : IEntry
/// </summary>
public abstract bool IsSplitAfter { get; }
public int VolumeIndexFirst => Parts?.FirstOrDefault()?.Index ?? 0;
public int VolumeIndexLast => Parts?.LastOrDefault()?.Index ?? 0;
public int VolumeIndexFirst => Parts.FirstOrDefault()?.Index ?? 0;
public int VolumeIndexLast => Parts.LastOrDefault()?.Index ?? 0;
/// <inheritdoc/>
public override string ToString() => Key;
public override string ToString() => Key ?? "Entry";
internal abstract IEnumerable<FilePart> Parts { get; }

View File

@@ -16,7 +16,7 @@ internal static class ExtractionMethods
)
{
string destinationFileName;
string fullDestinationDirectoryPath = Path.GetFullPath(destinationDirectory);
var fullDestinationDirectoryPath = Path.GetFullPath(destinationDirectory);
//check for trailing slash.
if (
@@ -36,11 +36,12 @@ internal static class ExtractionMethods
options ??= new ExtractionOptions() { Overwrite = true };
string file = Path.GetFileName(entry.Key);
var file = Path.GetFileName(entry.Key.NotNull("Entry Key is null")).NotNull("File is null");
if (options.ExtractFullPath)
{
string folder = Path.GetDirectoryName(entry.Key)!;
string destdir = Path.GetFullPath(Path.Combine(fullDestinationDirectoryPath, folder));
var folder = Path.GetDirectoryName(entry.Key.NotNull("Entry Key is null"))
.NotNull("Directory is null");
var destdir = Path.GetFullPath(Path.Combine(fullDestinationDirectoryPath, folder));
if (!Directory.Exists(destdir))
{
@@ -102,7 +103,7 @@ internal static class ExtractionMethods
}
else
{
FileMode fm = FileMode.Create;
var fm = FileMode.Create;
options ??= new ExtractionOptions() { Overwrite = true };
if (!options.Overwrite)

View File

@@ -8,7 +8,7 @@ public abstract class FilePart
internal ArchiveEncoding ArchiveEncoding { get; }
internal abstract string FilePartName { get; }
internal abstract string? FilePartName { get; }
public int Index { get; set; }
internal abstract Stream GetCompressedStream();

View File

@@ -6,23 +6,23 @@ namespace SharpCompress.Common.GZip;
public class GZipEntry : Entry
{
private readonly GZipFilePart _filePart;
private readonly GZipFilePart? _filePart;
internal GZipEntry(GZipFilePart filePart) => _filePart = filePart;
internal GZipEntry(GZipFilePart? filePart) => _filePart = filePart;
public override CompressionType CompressionType => CompressionType.GZip;
public override long Crc => _filePart.Crc ?? 0;
public override long Crc => _filePart?.Crc ?? 0;
public override string Key => _filePart.FilePartName;
public override string? Key => _filePart?.FilePartName;
public override string? LinkTarget => null;
public override long CompressedSize => 0;
public override long Size => _filePart.UncompressedSize ?? 0;
public override long Size => _filePart?.UncompressedSize ?? 0;
public override DateTime? LastModifiedTime => _filePart.DateModified;
public override DateTime? LastModifiedTime => _filePart?.DateModified;
public override DateTime? CreatedTime => null;
@@ -36,7 +36,7 @@ public class GZipEntry : Entry
public override bool IsSplitAfter => false;
internal override IEnumerable<FilePart> Parts => _filePart.AsEnumerable<FilePart>();
internal override IEnumerable<FilePart> Parts => _filePart.Empty();
internal static IEnumerable<GZipEntry> GetEntries(Stream stream, OptionsBase options)
{

View File

@@ -34,7 +34,7 @@ internal sealed class GZipFilePart : FilePart
internal uint? Crc { get; private set; }
internal uint? UncompressedSize { get; private set; }
internal override string FilePartName => _name!;
internal override string? FilePartName => _name;
internal override Stream GetCompressedStream() =>
new DeflateStream(_stream, CompressionMode.Decompress, CompressionLevel.Default);

View File

@@ -5,7 +5,7 @@ namespace SharpCompress.Common.GZip;
public class GZipVolume : Volume
{
public GZipVolume(Stream stream, ReaderOptions options, int index = 0)
public GZipVolume(Stream stream, ReaderOptions? options, int index)
: base(stream, options, index) { }
public GZipVolume(FileInfo fileInfo, ReaderOptions options)

View File

@@ -9,7 +9,7 @@ public interface IEntry
long CompressedSize { get; }
long Crc { get; }
DateTime? CreatedTime { get; }
string Key { get; }
string? Key { get; }
string? LinkTarget { get; }
bool IsDirectory { get; }
bool IsEncrypted { get; }

View File

@@ -6,5 +6,5 @@ public interface IVolume : IDisposable
{
int Index { get; }
string FileName { get; }
string? FileName { get; }
}

View File

@@ -7,5 +7,5 @@ public class OptionsBase
/// </summary>
public bool LeaveStreamOpen { get; set; } = true;
public ArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();
public ArchiveEncoding ArchiveEncoding { get; set; } = new();
}

View File

@@ -1,12 +0,0 @@
using System;
namespace SharpCompress.Common;
public class PasswordProtectedException : ExtractionException
{
public PasswordProtectedException(string message)
: base(message) { }
public PasswordProtectedException(string message, Exception inner)
: base(message, inner) { }
}

View File

@@ -0,0 +1,84 @@
#nullable disable
using System.Security.Cryptography;
using System.Text;
using SharpCompress.Common.Rar.Headers;
namespace SharpCompress.Common.Rar;
internal class CryptKey3 : ICryptKey
{
const int AES_128 = 128;
private string _password;
public CryptKey3(string password) => _password = password ?? "";
public ICryptoTransform Transformer(byte[] salt)
{
var aesIV = new byte[EncryptionConstV5.SIZE_INITV];
var rawLength = 2 * _password.Length;
var rawPassword = new byte[rawLength + EncryptionConstV5.SIZE_SALT30];
var passwordBytes = Encoding.UTF8.GetBytes(_password);
for (var i = 0; i < _password.Length; i++)
{
rawPassword[i * 2] = passwordBytes[i];
rawPassword[(i * 2) + 1] = 0;
}
for (var i = 0; i < salt.Length; i++)
{
rawPassword[i + rawLength] = salt[i];
}
var msgDigest = SHA1.Create();
const int noOfRounds = (1 << 18);
const int iblock = 3;
byte[] digest;
var data = new byte[(rawPassword.Length + iblock) * noOfRounds];
//TODO slow code below, find ways to optimize
for (var i = 0; i < noOfRounds; i++)
{
rawPassword.CopyTo(data, i * (rawPassword.Length + iblock));
data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 0] = (byte)i;
data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 1] = (byte)(i >> 8);
data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 2] = (byte)(i >> 16);
if (i % (noOfRounds / EncryptionConstV5.SIZE_INITV) == 0)
{
digest = msgDigest.ComputeHash(data, 0, (i + 1) * (rawPassword.Length + iblock));
aesIV[i / (noOfRounds / EncryptionConstV5.SIZE_INITV)] = digest[19];
}
}
digest = msgDigest.ComputeHash(data);
//slow code ends
var aesKey = new byte[EncryptionConstV5.SIZE_INITV];
for (var i = 0; i < 4; i++)
{
for (var j = 0; j < 4; j++)
{
aesKey[(i * 4) + j] = (byte)(
(
((digest[i * 4] * 0x1000000) & 0xff000000)
| (uint)((digest[(i * 4) + 1] * 0x10000) & 0xff0000)
| (uint)((digest[(i * 4) + 2] * 0x100) & 0xff00)
| (uint)(digest[(i * 4) + 3] & 0xff)
) >> (j * 8)
);
}
}
var aes = Aes.Create();
aes.KeySize = AES_128;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
aes.Key = aesKey;
aes.IV = aesIV;
return aes.CreateDecryptor();
}
}

View File

@@ -0,0 +1,95 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using SharpCompress.Common.Rar.Headers;
namespace SharpCompress.Common.Rar;
internal class CryptKey5 : ICryptKey
{
const int AES_256 = 256;
const int DERIVED_KEY_LENGTH = 0x10;
const int SHA256_DIGEST_SIZE = 32;
private string _password;
private Rar5CryptoInfo _cryptoInfo;
private byte[] _pswCheck = { };
private byte[] _hashKey = { };
public CryptKey5(string password, Rar5CryptoInfo rar5CryptoInfo)
{
_password = password ?? "";
_cryptoInfo = rar5CryptoInfo;
}
public byte[] PswCheck => _pswCheck;
public byte[] HashKey => _hashKey;
private static List<byte[]> GenerateRarPBKDF2Key(
string password,
byte[] salt,
int iterations,
int keyLength
)
{
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(password));
var block = hmac.ComputeHash(salt);
var finalHash = (byte[])block.Clone();
var loop = new int[] { iterations, 17, 17 };
var res = new List<byte[]> { };
for (var x = 0; x < 3; x++)
{
for (var i = 1; i < loop[x]; i++)
{
block = hmac.ComputeHash(block);
for (var j = 0; j < finalHash.Length; j++)
{
finalHash[j] ^= block[j];
}
}
res.Add((byte[])finalHash.Clone());
}
return res;
}
public ICryptoTransform Transformer(byte[] salt)
{
var iterations = (1 << _cryptoInfo.LG2Count); // Adjust the number of iterations as needed
var salt_rar5 = salt.Concat(new byte[] { 0, 0, 0, 1 });
var derivedKey = GenerateRarPBKDF2Key(
_password,
salt_rar5.ToArray(),
iterations,
DERIVED_KEY_LENGTH
);
_hashKey = derivedKey[1];
_pswCheck = new byte[EncryptionConstV5.SIZE_PSWCHECK];
for (var i = 0; i < SHA256_DIGEST_SIZE; i++)
{
_pswCheck[i % EncryptionConstV5.SIZE_PSWCHECK] ^= derivedKey[2][i];
}
if (_cryptoInfo.UsePswCheck && !_cryptoInfo.PswCheck.SequenceEqual(_pswCheck))
{
throw new CryptographicException("The password did not match.");
}
var aes = Aes.Create();
aes.KeySize = AES_256;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.None;
aes.Key = derivedKey[0];
aes.IV = _cryptoInfo.InitV;
return aes.CreateDecryptor();
}
}

View File

@@ -1,50 +1,19 @@
#nullable disable
using System;
using System.Security.Cryptography;
using SharpCompress.Common.Rar.Headers;
using SharpCompress.IO;
namespace SharpCompress.Common.Rar.Headers;
internal class ArchiveCryptHeader : RarHeader
{
private const int CRYPT_VERSION = 0; // Supported encryption version.
private const int SIZE_SALT50 = 16;
private const int SIZE_PSWCHECK = 8;
private const int SIZE_PSWCHECK_CSUM = 4;
private const int CRYPT5_KDF_LG2_COUNT_MAX = 24; // LOG2 of maximum accepted iteration count.
private bool _usePswCheck;
private uint _lg2Count; // Log2 of PBKDF2 repetition count.
private byte[] _salt;
private byte[] _pswCheck;
private byte[] _pswCheckCsm;
public ArchiveCryptHeader(RarHeader header, RarCrcBinaryReader reader)
: base(header, reader, HeaderType.Crypt) { }
protected override void ReadFinish(MarkingBinaryReader reader)
{
var cryptVersion = reader.ReadRarVIntUInt32();
if (cryptVersion > CRYPT_VERSION)
{
//error?
return;
}
var encryptionFlags = reader.ReadRarVIntUInt32();
_usePswCheck = FlagUtility.HasFlag(encryptionFlags, EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK);
_lg2Count = reader.ReadRarVIntByte(1);
public Rar5CryptoInfo CryptInfo = new();
//UsePswCheck = HasHeaderFlag(EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK);
if (_lg2Count > CRYPT5_KDF_LG2_COUNT_MAX)
{
//error?
return;
}
_salt = reader.ReadBytes(SIZE_SALT50);
if (_usePswCheck)
{
_pswCheck = reader.ReadBytes(SIZE_PSWCHECK);
_pswCheckCsm = reader.ReadBytes(SIZE_PSWCHECK_CSUM);
}
}
protected override void ReadFinish(MarkingBinaryReader reader) =>
CryptInfo = new Rar5CryptoInfo(reader, false);
}

View File

@@ -2,6 +2,8 @@
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using SharpCompress.IO;
#if !Rar2017_64bit
@@ -16,7 +18,7 @@ namespace SharpCompress.Common.Rar.Headers;
internal class FileHeader : RarHeader
{
private uint _fileCrc;
private byte[] _hash;
public FileHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType)
: base(header, reader, headerType) { }
@@ -51,7 +53,7 @@ internal class FileHeader : RarHeader
if (HasFlag(FileFlagsV5.HAS_CRC32))
{
FileCrc = reader.ReadUInt32();
FileCrc = reader.ReadBytes(4);
}
var compressionInfo = reader.ReadRarVIntUInt16();
@@ -103,7 +105,13 @@ internal class FileHeader : RarHeader
throw new InvalidFormatException("rar5 header size / extra size inconsistency");
}
isEncryptedRar5 = false;
const ushort FHEXTRA_CRYPT = 0x01;
const ushort FHEXTRA_HASH = 0x02;
const ushort FHEXTRA_HTIME = 0x03;
// const ushort FHEXTRA_VERSION = 0x04;
const ushort FHEXTRA_REDIR = 0x05;
// const ushort FHEXTRA_UOWNER = 0x06;
// const ushort FHEXTRA_SUBDATA = 0x07;
while (RemainingHeaderBytes(reader) > 0)
{
@@ -112,23 +120,30 @@ internal class FileHeader : RarHeader
var type = reader.ReadRarVIntUInt16();
switch (type)
{
//TODO
case 1: // file encryption
case FHEXTRA_CRYPT: // file encryption
{
isEncryptedRar5 = true;
Rar5CryptoInfo = new Rar5CryptoInfo(reader, true);
//var version = reader.ReadRarVIntByte();
//if (version != 0) throw new InvalidFormatException("unknown encryption algorithm " + version);
if (Rar5CryptoInfo.PswCheck.All(singleByte => singleByte == 0))
{
Rar5CryptoInfo = null;
}
}
break;
// case 2: // file hash
// {
//
// }
// break;
case 3: // file time
case FHEXTRA_HASH:
{
const uint FHEXTRA_HASH_BLAKE2 = 0x0;
// const uint HASH_BLAKE2 = 0x03;
const int BLAKE2_DIGEST_SIZE = 0x20;
if ((uint)reader.ReadRarVInt() == FHEXTRA_HASH_BLAKE2)
{
// var hash = HASH_BLAKE2;
_hash = reader.ReadBytes(BLAKE2_DIGEST_SIZE);
}
// enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2};
}
break;
case FHEXTRA_HTIME: // file time
{
var flags = reader.ReadRarVIntUInt16();
var isWindowsTime = (flags & 1) == 0;
@@ -147,22 +162,27 @@ internal class FileHeader : RarHeader
}
break;
//TODO
// case 4: // file version
// case FHEXTRA_VERSION: // file version
// {
//
// }
// break;
// case 5: // file system redirection
case FHEXTRA_REDIR: // file system redirection
{
RedirType = reader.ReadRarVIntByte();
RedirFlags = reader.ReadRarVIntByte();
var nn = reader.ReadRarVIntUInt16();
var bb = reader.ReadBytes(nn);
RedirTargetName = ConvertPathV5(Encoding.UTF8.GetString(bb, 0, bb.Length));
}
break;
//TODO
// case FHEXTRA_UOWNER: // unix owner
// {
//
// }
// break;
// case 6: // unix owner
// {
//
// }
// break;
// case 7: // service data
// case FHEXTRA_SUBDATA: // service data
// {
//
// }
@@ -170,6 +190,7 @@ internal class FileHeader : RarHeader
default:
// skip unknown record types to allow new record types to be added in the future
//Console.WriteLine($"unhandled rar header field type {type}");
break;
}
// drain any trailing bytes of extra record
@@ -221,7 +242,7 @@ internal class FileHeader : RarHeader
HostOs = reader.ReadByte();
FileCrc = reader.ReadUInt32();
FileCrc = reader.ReadBytes(4);
FileLastModifiedTime = Utility.DosDateToDateTime(reader.ReadUInt32());
@@ -254,13 +275,11 @@ internal class FileHeader : RarHeader
var fileNameBytes = reader.ReadBytes(nameSize);
const int saltSize = 8;
const int newLhdSize = 32;
switch (HeaderCode)
{
case HeaderCodeV.RAR4_FILE_HEADER:
{
if (HasFlag(FileFlagsV4.UNICODE))
{
@@ -287,12 +306,11 @@ internal class FileHeader : RarHeader
}
break;
case HeaderCodeV.RAR4_NEW_SUB_HEADER:
{
var datasize = HeaderSize - newLhdSize - nameSize;
if (HasFlag(FileFlagsV4.SALT))
{
datasize -= saltSize;
datasize -= EncryptionConstV5.SIZE_SALT30;
}
if (datasize > 0)
{
@@ -313,7 +331,7 @@ internal class FileHeader : RarHeader
if (HasFlag(FileFlagsV4.SALT))
{
R4Salt = reader.ReadBytes(saltSize);
R4Salt = reader.ReadBytes(EncryptionConstV5.SIZE_SALT30);
}
if (HasFlag(FileFlagsV4.EXT_TIME))
{
@@ -394,18 +412,10 @@ internal class FileHeader : RarHeader
private bool HasFlag(ushort flag) => (Flags & flag) == flag;
internal uint FileCrc
internal byte[] FileCrc
{
get
{
if (IsRar5 && !HasFlag(FileFlagsV5.HAS_CRC32))
{
//!!! rar5:
throw new InvalidOperationException("TODO rar5");
}
return _fileCrc;
}
private set => _fileCrc = value;
get => _hash;
private set => _hash = value;
}
// 0 - storing
@@ -427,11 +437,17 @@ internal class FileHeader : RarHeader
public bool IsSolid { get; private set; }
public byte RedirType { get; private set; }
public bool IsRedir => RedirType != 0;
public byte RedirFlags { get; private set; }
public bool IsRedirDirectory => (RedirFlags & RedirFlagV5.DIRECTORY) != 0;
public string RedirTargetName { get; private set; }
// unused for UnpackV1 implementation (limitation)
internal size_t WindowSize { get; private set; }
internal byte[] R4Salt { get; private set; }
internal Rar5CryptoInfo Rar5CryptoInfo { get; private set; }
private byte HostOs { get; set; }
internal uint FileAttributes { get; private set; }
internal long CompressedSize { get; private set; }
@@ -449,8 +465,7 @@ internal class FileHeader : RarHeader
public bool IsDirectory => HasFlag(IsRar5 ? FileFlagsV5.DIRECTORY : FileFlagsV4.DIRECTORY);
private bool isEncryptedRar5 = false;
public bool IsEncrypted => IsRar5 ? isEncryptedRar5 : HasFlag(FileFlagsV4.PASSWORD);
public bool IsEncrypted => IsRar5 ? Rar5CryptoInfo != null : HasFlag(FileFlagsV4.PASSWORD);
internal DateTime? FileLastModifiedTime { get; private set; }

View File

@@ -50,6 +50,17 @@ internal static class EncryptionFlagsV5
public const uint FHEXTRA_CRYPT_HASHMAC = 0x02;
}
internal static class EncryptionConstV5
{
public const int VERSION = 0;
public const uint CRYPT5_KDF_LG2_COUNT_MAX = 0x24;
public const int SIZE_SALT30 = 0x08;
public const int SIZE_SALT50 = 0x10;
public const int SIZE_INITV = 0x10;
public const int SIZE_PSWCHECK = 0x08;
public const int SIZE_PSWCHECK_CSUM = 0x04;
}
internal static class HeaderFlagsV5
{
public const ushort HAS_EXTRA = 0x0001;
@@ -146,3 +157,17 @@ internal static class EndArchiveFlagsV5
{
public const ushort HAS_NEXT_VOLUME = 0x0001;
}
internal static class RedirTypeV5
{
public const byte UNIX_SYMLINK = 0x0001;
public const byte WIN_SYMLINK = 0x0002;
public const byte WIN_JUNCTION = 0x0003;
public const byte HARD_LINK = 0x0004;
public const byte FILE_COPY = 0x0005;
}
internal static class RedirFlagV5
{
public const byte DIRECTORY = 0x0001;
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using SharpCompress.IO;

View File

@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Common.Rar;
using SharpCompress.IO;
using SharpCompress.Readers;
@@ -9,6 +11,8 @@ public class RarHeaderFactory
{
private bool _isRar5;
private Rar5CryptoInfo? _cryptInfo;
public RarHeaderFactory(StreamingMode mode, ReaderOptions options)
{
StreamingMode = mode;
@@ -53,7 +57,19 @@ public class RarHeaderFactory
"Encrypted Rar archive has no password specified."
);
}
reader = new RarCryptoBinaryReader(stream, Options.Password);
if (_isRar5 && _cryptInfo != null)
{
_cryptInfo.ReadInitV(new MarkingBinaryReader(stream));
var _headerKey = new CryptKey5(Options.Password!, _cryptInfo);
reader = new RarCryptoBinaryReader(stream, _headerKey, _cryptInfo.Salt);
}
else
{
var key = new CryptKey3(Options.Password);
reader = new RarCryptoBinaryReader(stream, key);
}
}
var header = RarHeader.TryReadBase(reader, _isRar5, Options.ArchiveEncoding);
@@ -82,13 +98,11 @@ public class RarHeaderFactory
switch (StreamingMode)
{
case StreamingMode.Seekable:
{
reader.BaseStream.Position += ph.DataSize;
}
break;
case StreamingMode.Streaming:
{
reader.BaseStream.Skip(ph.DataSize);
}
@@ -105,7 +119,14 @@ public class RarHeaderFactory
case HeaderCodeV.RAR5_SERVICE_HEADER:
{
var fh = new FileHeader(header, reader, HeaderType.Service);
SkipData(fh, reader);
if (fh.FileName == "CMT")
{
fh.PackedStream = new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize);
}
else
{
SkipData(fh, reader);
}
return fh;
}
@@ -123,17 +144,15 @@ public class RarHeaderFactory
switch (StreamingMode)
{
case StreamingMode.Seekable:
{
fh.DataStartPosition = reader.BaseStream.Position;
reader.BaseStream.Position += fh.CompressedSize;
}
break;
case StreamingMode.Streaming:
{
var ms = new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize);
if (fh.R4Salt is null)
if (fh.R4Salt is null && fh.Rar5CryptoInfo is null)
{
fh.PackedStream = ms;
}
@@ -141,8 +160,10 @@ public class RarHeaderFactory
{
fh.PackedStream = new RarCryptoWrapper(
ms,
Options.Password!,
fh.R4Salt
fh.R4Salt is null ? fh.Rar5CryptoInfo.Salt : fh.R4Salt,
fh.R4Salt is null
? new CryptKey5(Options.Password!, fh.Rar5CryptoInfo)
: new CryptKey3(Options.Password!)
);
}
}
@@ -161,9 +182,11 @@ public class RarHeaderFactory
}
case HeaderCodeV.RAR5_ARCHIVE_ENCRYPTION_HEADER:
{
var ch = new ArchiveCryptHeader(header, reader);
var cryptoHeader = new ArchiveCryptHeader(header, reader);
IsEncrypted = true;
return ch;
_cryptInfo = cryptoHeader.CryptInfo;
return cryptoHeader;
}
default:
{
@@ -177,14 +200,12 @@ public class RarHeaderFactory
switch (StreamingMode)
{
case StreamingMode.Seekable:
{
fh.DataStartPosition = reader.BaseStream.Position;
reader.BaseStream.Position += fh.CompressedSize;
}
break;
case StreamingMode.Streaming:
{
//skip the data because it's useless?
reader.BaseStream.Skip(fh.CompressedSize);

View File

@@ -0,0 +1,8 @@
using System.Security.Cryptography;
namespace SharpCompress.Common.Rar;
internal interface ICryptKey
{
ICryptoTransform Transformer(byte[] salt);
}

View File

@@ -0,0 +1,57 @@
using System;
using System.Security.Cryptography;
using SharpCompress.Common.Rar.Headers;
using SharpCompress.IO;
namespace SharpCompress.Common.Rar;
internal class Rar5CryptoInfo
{
public Rar5CryptoInfo() { }
public Rar5CryptoInfo(MarkingBinaryReader reader, bool readInitV)
{
var cryptVersion = reader.ReadRarVIntUInt32();
if (cryptVersion > EncryptionConstV5.VERSION)
{
throw new CryptographicException($"Unsupported crypto version of {cryptVersion}");
}
var encryptionFlags = reader.ReadRarVIntUInt32();
UsePswCheck = FlagUtility.HasFlag(encryptionFlags, EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK);
LG2Count = reader.ReadRarVIntByte(1);
if (LG2Count > EncryptionConstV5.CRYPT5_KDF_LG2_COUNT_MAX)
{
throw new CryptographicException($"Unsupported LG2 count of {LG2Count}.");
}
Salt = reader.ReadBytes(EncryptionConstV5.SIZE_SALT50);
if (readInitV) // File header needs to read IV here
{
ReadInitV(reader);
}
if (UsePswCheck)
{
PswCheck = reader.ReadBytes(EncryptionConstV5.SIZE_PSWCHECK);
var _pswCheckCsm = reader.ReadBytes(EncryptionConstV5.SIZE_PSWCHECK_CSUM);
var sha = SHA256.Create();
UsePswCheck = sha.ComputeHash(PswCheck).AsSpan().StartsWith(_pswCheckCsm.AsSpan());
}
}
public void ReadInitV(MarkingBinaryReader reader) =>
InitV = reader.ReadBytes(EncryptionConstV5.SIZE_INITV);
public bool UsePswCheck = false;
public int LG2Count = 0;
public byte[] InitV = { };
public byte[] Salt = { };
public byte[] PswCheck = { };
}

View File

@@ -1,29 +1,29 @@
using System.Collections.Generic;
#nullable disable
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common.Rar.Headers;
using SharpCompress.Crypto;
namespace SharpCompress.Common.Rar;
internal sealed class RarCryptoBinaryReader : RarCrcBinaryReader
{
private RarRijndael _rijndael;
private byte[] _salt;
private readonly string _password;
private readonly Queue<byte> _data = new Queue<byte>();
private BlockTransformer _rijndael;
private readonly Queue<byte> _data = new();
private long _readCount;
public RarCryptoBinaryReader(Stream stream, string password)
public RarCryptoBinaryReader(Stream stream, ICryptKey cryptKey)
: base(stream)
{
_password = password;
// coderb: not sure why this was being done at this logical point
//SkipQueue();
var salt = ReadBytes(8);
_salt = salt;
_rijndael = RarRijndael.InitializeFrom(_password, salt);
var salt = base.ReadBytes(EncryptionConstV5.SIZE_SALT30);
_readCount += EncryptionConstV5.SIZE_SALT30;
_rijndael = new BlockTransformer(cryptKey.Transformer(salt));
}
public RarCryptoBinaryReader(Stream stream, ICryptKey cryptKey, byte[] salt)
: base(stream) => _rijndael = new BlockTransformer(cryptKey.Transformer(salt));
// track read count ourselves rather than using the underlying stream since we buffer
public override long CurrentReadByteCount
{
@@ -36,29 +36,9 @@ internal sealed class RarCryptoBinaryReader : RarCrcBinaryReader
public override void Mark() => _readCount = 0;
private bool UseEncryption => _salt != null;
public override byte ReadByte() => ReadAndDecryptBytes(1)[0];
public override byte ReadByte()
{
if (UseEncryption)
{
return ReadAndDecryptBytes(1)[0];
}
_readCount++;
return base.ReadByte();
}
public override byte[] ReadBytes(int count)
{
if (UseEncryption)
{
return ReadAndDecryptBytes(count);
}
_readCount += count;
return base.ReadBytes(count);
}
public override byte[] ReadBytes(int count) => ReadAndDecryptBytes(count);
private byte[] ReadAndDecryptBytes(int count)
{

View File

@@ -1,37 +1,30 @@
using System;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Crypto;
namespace SharpCompress.Common.Rar;
internal sealed class RarCryptoWrapper : Stream
{
private readonly Stream _actualStream;
private readonly byte[] _salt;
private RarRijndael _rijndael;
private readonly Queue<byte> _data = new Queue<byte>();
private BlockTransformer _rijndael;
private readonly Queue<byte> _data = new();
public RarCryptoWrapper(Stream actualStream, string password, byte[] salt)
public RarCryptoWrapper(Stream actualStream, byte[] salt, ICryptKey key)
{
_actualStream = actualStream;
_salt = salt;
_rijndael = RarRijndael.InitializeFrom(password ?? "", salt);
_rijndael = new BlockTransformer(key.Transformer(salt));
}
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
public override void SetLength(long value) => throw new NotSupportedException();
public override int Read(byte[] buffer, int offset, int count)
{
if (_salt is null)
{
return _actualStream.Read(buffer, offset, count);
}
return ReadAndDecrypt(buffer, offset, count);
}
public override int Read(byte[] buffer, int offset, int count) =>
ReadAndDecrypt(buffer, offset, count);
public int ReadAndDecrypt(byte[] buffer, int offset, int count)
{
@@ -41,7 +34,7 @@ internal sealed class RarCryptoWrapper : Stream
if (sizeToRead > 0)
{
var alignedSize = sizeToRead + ((~sizeToRead + 1) & 0xf);
Span<byte> cipherText = stackalloc byte[RarRijndael.CRYPTO_BLOCK_SIZE];
Span<byte> cipherText = stackalloc byte[16];
for (var i = 0; i < alignedSize / 16; i++)
{
//long ax = System.currentTimeMillis();
@@ -77,11 +70,11 @@ internal sealed class RarCryptoWrapper : Stream
protected override void Dispose(bool disposing)
{
if (_rijndael != null)
if (disposing)
{
_rijndael.Dispose();
_rijndael = null!;
}
base.Dispose(disposing);
}
}

View File

@@ -20,12 +20,12 @@ public abstract class RarEntry : Entry
/// <summary>
/// The File's 32 bit CRC Hash
/// </summary>
public override long Crc => FileHeader.FileCrc;
public override long Crc => BitConverter.ToUInt32(FileHeader.FileCrc, 0);
/// <summary>
/// The path of the file internal to the Rar Archive.
/// </summary>
public override string Key => FileHeader.FileName;
public override string? Key => FileHeader.FileName;
public override string? LinkTarget => null;
@@ -55,12 +55,21 @@ public abstract class RarEntry : Entry
public override bool IsEncrypted => FileHeader.IsEncrypted;
/// <summary>
/// Entry is password protected and encrypted and cannot be extracted.
/// Entry Windows file attributes
/// </summary>
public override int? Attrib => (int)FileHeader.FileAttributes;
/// <summary>
/// Entry is a directory
/// </summary>
public override bool IsDirectory => FileHeader.IsDirectory;
public override bool IsSplitAfter => FileHeader.IsSplitAfter;
public bool IsRedir => FileHeader.IsRedir;
public string RedirTargetName => FileHeader.RedirTargetName;
public override string ToString() =>
string.Format(
"Entry Path: {0} Compressed Size: {1} Uncompressed Size: {2} CRC: {3}",

View File

@@ -1,114 +0,0 @@
#nullable disable
using System;
using System.Security.Cryptography;
using System.Text;
using SharpCompress.Crypto;
namespace SharpCompress.Common.Rar;
internal class RarRijndael : IDisposable
{
internal const int CRYPTO_BLOCK_SIZE = 16;
private readonly string _password;
private readonly byte[] _salt;
private byte[] _aesInitializationVector;
private RijndaelEngine _rijndael;
private RarRijndael(string password, byte[] salt)
{
_password = password;
_salt = salt;
}
private void Initialize()
{
_rijndael = new RijndaelEngine();
_aesInitializationVector = new byte[CRYPTO_BLOCK_SIZE];
var rawLength = 2 * _password.Length;
var rawPassword = new byte[rawLength + 8];
var passwordBytes = Encoding.UTF8.GetBytes(_password);
for (var i = 0; i < _password.Length; i++)
{
rawPassword[i * 2] = passwordBytes[i];
rawPassword[(i * 2) + 1] = 0;
}
for (var i = 0; i < _salt.Length; i++)
{
rawPassword[i + rawLength] = _salt[i];
}
const int noOfRounds = (1 << 18);
const int iblock = 3;
byte[] digest;
var data = new byte[(rawPassword.Length + iblock) * noOfRounds];
//TODO slow code below, find ways to optimize
for (var i = 0; i < noOfRounds; i++)
{
rawPassword.CopyTo(data, i * (rawPassword.Length + iblock));
data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 0] = (byte)i;
data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 1] = (byte)(i >> 8);
data[(i * (rawPassword.Length + iblock)) + rawPassword.Length + 2] = (byte)(
i >> CRYPTO_BLOCK_SIZE
);
if (i % (noOfRounds / CRYPTO_BLOCK_SIZE) == 0)
{
digest = SHA1.Create()
.ComputeHash(data, 0, (i + 1) * (rawPassword.Length + iblock));
_aesInitializationVector[i / (noOfRounds / CRYPTO_BLOCK_SIZE)] = digest[19];
}
}
digest = SHA1.Create().ComputeHash(data);
//slow code ends
var aesKey = new byte[CRYPTO_BLOCK_SIZE];
for (var i = 0; i < 4; i++)
{
for (var j = 0; j < 4; j++)
{
aesKey[(i * 4) + j] = (byte)(
(
((digest[i * 4] * 0x1000000) & 0xff000000)
| (uint)((digest[(i * 4) + 1] * 0x10000) & 0xff0000)
| (uint)((digest[(i * 4) + 2] * 0x100) & 0xff00)
| (uint)(digest[(i * 4) + 3] & 0xff)
) >> (j * 8)
);
}
}
_rijndael.Init(false, new KeyParameter(aesKey));
}
public static RarRijndael InitializeFrom(string password, byte[] salt)
{
var rijndael = new RarRijndael(password, salt);
rijndael.Initialize();
return rijndael;
}
public byte[] ProcessBlock(ReadOnlySpan<byte> cipherText)
{
Span<byte> plainText = stackalloc byte[CRYPTO_BLOCK_SIZE]; // 16 bytes
var decryptedBytes = new byte[CRYPTO_BLOCK_SIZE];
_rijndael.ProcessBlock(cipherText, plainText);
for (var j = 0; j < CRYPTO_BLOCK_SIZE; j++)
{
decryptedBytes[j] = (byte)(plainText[j] ^ _aesInitializationVector[j % 16]); //32:114, 33:101
}
for (var j = 0; j < _aesInitializationVector.Length; j++)
{
_aesInitializationVector[j] = cipherText[j];
}
return decryptedBytes;
}
public void Dispose() { }
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using SharpCompress.Common.Rar.Headers;
using SharpCompress.IO;
using SharpCompress.Readers;
@@ -14,17 +15,14 @@ namespace SharpCompress.Common.Rar;
public abstract class RarVolume : Volume
{
private readonly RarHeaderFactory _headerFactory;
internal int _maxCompressionAlgorithm;
private int _maxCompressionAlgorithm;
internal RarVolume(StreamingMode mode, Stream stream, ReaderOptions options, int index = 0)
internal RarVolume(StreamingMode mode, Stream stream, ReaderOptions options, int index)
: base(stream, options, index) => _headerFactory = new RarHeaderFactory(mode, options);
#nullable disable
internal ArchiveHeader ArchiveHeader { get; private set; }
private ArchiveHeader? ArchiveHeader { get; set; }
#nullable enable
internal StreamingMode Mode => _headerFactory.StreamingMode;
private StreamingMode Mode => _headerFactory.StreamingMode;
internal abstract IEnumerable<RarFilePart> ReadFileParts();
@@ -38,19 +36,16 @@ public abstract class RarVolume : Volume
switch (header.HeaderType)
{
case HeaderType.Mark:
{
lastMarkHeader = (MarkHeader)header;
}
break;
case HeaderType.Archive:
{
ArchiveHeader = (ArchiveHeader)header;
}
break;
case HeaderType.File:
{
var fh = (FileHeader)header;
if (_maxCompressionAlgorithm < fh.CompressionAlgorithm)
@@ -62,7 +57,6 @@ public abstract class RarVolume : Volume
}
break;
case HeaderType.Service:
{
var fh = (FileHeader)header;
if (fh.FileName == "CMT")
@@ -70,11 +64,7 @@ public abstract class RarVolume : Volume
var part = CreateFilePart(lastMarkHeader!, fh);
var buffer = new byte[fh.CompressedSize];
part.GetCompressedStream().Read(buffer, 0, buffer.Length);
Comment = System
.Text
.Encoding
.UTF8
.GetString(buffer, 0, buffer.Length - 1);
Comment = Encoding.UTF8.GetString(buffer, 0, buffer.Length - 1);
}
}
break;
@@ -108,7 +98,7 @@ public abstract class RarVolume : Volume
get
{
EnsureArchiveHeaderLoaded();
return ArchiveHeader.IsFirstVolume;
return ArchiveHeader?.IsFirstVolume ?? false;
}
}
@@ -120,7 +110,7 @@ public abstract class RarVolume : Volume
get
{
EnsureArchiveHeaderLoaded();
return ArchiveHeader.IsVolume;
return ArchiveHeader?.IsVolume ?? false;
}
}
@@ -133,7 +123,7 @@ public abstract class RarVolume : Volume
get
{
EnsureArchiveHeaderLoaded();
return ArchiveHeader.IsSolid;
return ArchiveHeader?.IsSolid ?? false;
}
}

View File

@@ -15,15 +15,15 @@ internal class ArchiveDatabase
internal long _startPositionAfterHeader;
internal long _dataStartPosition;
internal List<long> _packSizes = new List<long>();
internal List<uint?> _packCrCs = new List<uint?>();
internal List<CFolder> _folders = new List<CFolder>();
internal List<long> _packSizes = new();
internal List<uint?> _packCrCs = new();
internal List<CFolder> _folders = new();
internal List<int> _numUnpackStreamsVector;
internal List<CFileItem> _files = new List<CFileItem>();
internal List<CFileItem> _files = new();
internal List<long> _packStreamStartPositions = new List<long>();
internal List<int> _folderStartFileIndex = new List<int>();
internal List<int> _fileIndexToFolderIndexMap = new List<int>();
internal List<long> _packStreamStartPositions = new();
internal List<int> _folderStartFileIndex = new();
internal List<int> _fileIndexToFolderIndexMap = new();
internal IPasswordProvider PasswordProvider { get; }
@@ -35,7 +35,7 @@ internal class ArchiveDatabase
_packSizes.Clear();
_packCrCs.Clear();
_folders.Clear();
_numUnpackStreamsVector = null!;
_numUnpackStreamsVector = null;
_files.Clear();
_packStreamStartPositions.Clear();

View File

@@ -14,13 +14,13 @@ namespace SharpCompress.Common.SevenZip;
internal class ArchiveReader
{
internal Stream _stream;
internal Stack<DataReader> _readerStack = new Stack<DataReader>();
internal Stack<DataReader> _readerStack = new();
internal DataReader _currentReader;
internal long _streamOrigin;
internal long _streamEnding;
internal byte[] _header;
private readonly Dictionary<int, Stream> _cachedStreams = new Dictionary<int, Stream>();
private readonly Dictionary<int, Stream> _cachedStreams = new();
internal void AddByteStream(byte[] buffer, int offset, int length)
{
@@ -1359,7 +1359,7 @@ internal class ArchiveReader
{
internal int _fileIndex;
internal int _folderIndex;
internal List<bool> _extractStatuses = new List<bool>();
internal List<bool> _extractStatuses = new();
internal CExtractFolderInfo(int fileIndex, int folderIndex)
{
@@ -1393,7 +1393,7 @@ internal class ArchiveReader
public override bool CanWrite => false;
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Length => throw new NotSupportedException();

View File

@@ -6,11 +6,11 @@ namespace SharpCompress.Common.SevenZip;
internal class CFolder
{
internal List<CCoderInfo> _coders = new List<CCoderInfo>();
internal List<CBindPair> _bindPairs = new List<CBindPair>();
internal List<int> _packStreams = new List<int>();
internal List<CCoderInfo> _coders = new();
internal List<CBindPair> _bindPairs = new();
internal List<int> _packStreams = new();
internal int _firstPackStreamId;
internal List<long> _unpackSizes = new List<long>();
internal List<long> _unpackSizes = new();
internal uint? _unpackCrc;
internal bool UnpackCrcDefined => _unpackCrc != null;

View File

@@ -7,10 +7,10 @@ internal readonly struct CMethodId
public const ulong K_LZMA2_ID = 0x21;
public const ulong K_AES_ID = 0x06F10701;
public static readonly CMethodId K_COPY = new CMethodId(K_COPY_ID);
public static readonly CMethodId K_LZMA = new CMethodId(K_LZMA_ID);
public static readonly CMethodId K_LZMA2 = new CMethodId(K_LZMA2_ID);
public static readonly CMethodId K_AES = new CMethodId(K_AES_ID);
public static readonly CMethodId K_COPY = new(K_COPY_ID);
public static readonly CMethodId K_LZMA = new(K_LZMA_ID);
public static readonly CMethodId K_LZMA2 = new(K_LZMA2_ID);
public static readonly CMethodId K_AES = new(K_AES_ID);
public readonly ulong _id;

View File

@@ -13,7 +13,7 @@ public class SevenZipEntry : Entry
public override long Crc => FilePart.Header.Crc ?? 0;
public override string Key => FilePart.Header.Name;
public override string? Key => FilePart.Header.Name;
public override string? LinkTarget => null;

View File

@@ -41,7 +41,7 @@ internal class SevenZipFilePart : FilePart
{
if (!Header.HasStream)
{
return null!;
throw new InvalidOperationException("File does not have a stream.");
}
var folderStream = _database.GetFolderStream(_stream, Folder!, _database.PasswordProvider);
@@ -73,34 +73,24 @@ internal class SevenZipFilePart : FilePart
private const uint K_PPMD = 0x030401;
private const uint K_B_ZIP2 = 0x040202;
internal CompressionType GetCompression()
private CompressionType GetCompression()
{
if (Header.IsDir)
return CompressionType.None;
var coder = Folder!._coders.First();
switch (coder._methodId._id)
{
case K_LZMA:
case K_LZMA2:
{
return CompressionType.LZMA;
}
case K_PPMD:
{
return CompressionType.PPMd;
}
case K_B_ZIP2:
{
return CompressionType.BZip2;
}
default:
throw new NotImplementedException();
return CompressionType.None;
}
var coder = Folder.NotNull()._coders.First();
return coder._methodId._id switch
{
K_LZMA or K_LZMA2 => CompressionType.LZMA,
K_PPMD => CompressionType.PPMd,
K_B_ZIP2 => CompressionType.BZip2,
_ => throw new NotImplementedException()
};
}
internal bool IsEncrypted =>
Header.IsDir
? false
: Folder!._coders.FindIndex(c => c._methodId._id == CMethodId.K_AES_ID) != -1;
!Header.IsDir
&& Folder?._coders.FindIndex(c => c._methodId._id == CMethodId.K_AES_ID) != -1;
}

View File

@@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Buffers.Binary;
using System.IO;
@@ -9,12 +7,12 @@ namespace SharpCompress.Common.Tar.Headers;
internal sealed class TarHeader
{
internal static readonly DateTime EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
internal static readonly DateTime EPOCH = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public TarHeader(ArchiveEncoding archiveEncoding) => ArchiveEncoding = archiveEncoding;
internal string Name { get; set; }
internal string LinkName { get; set; }
internal string? Name { get; set; }
internal string? LinkName { get; set; }
internal long Mode { get; set; }
internal long UserId { get; set; }
@@ -22,7 +20,7 @@ internal sealed class TarHeader
internal long Size { get; set; }
internal DateTime LastModifiedTime { get; set; }
internal EntryType EntryType { get; set; }
internal Stream PackedStream { get; set; }
internal Stream? PackedStream { get; set; }
internal ArchiveEncoding ArchiveEncoding { get; }
internal const int BLOCK_SIZE = 512;
@@ -36,7 +34,9 @@ internal sealed class TarHeader
WriteOctalBytes(0, buffer, 116, 8); // group ID
//ArchiveEncoding.UTF8.GetBytes("magic").CopyTo(buffer, 257);
var nameByteCount = ArchiveEncoding.GetEncoding().GetByteCount(Name);
var nameByteCount = ArchiveEncoding
.GetEncoding()
.GetByteCount(Name.NotNull("Name is null"));
if (nameByteCount > 100)
{
// Set mock filename and filetype to indicate the next block is the actual name of the file
@@ -46,7 +46,7 @@ internal sealed class TarHeader
}
else
{
WriteStringBytes(ArchiveEncoding.Encode(Name), buffer, 100);
WriteStringBytes(ArchiveEncoding.Encode(Name.NotNull("Name is null")), buffer, 100);
WriteOctalBytes(Size, buffer, 124, 12);
var time = (long)(LastModifiedTime.ToUniversalTime() - EPOCH).TotalSeconds;
WriteOctalBytes(time, buffer, 136, 12);
@@ -77,7 +77,7 @@ internal sealed class TarHeader
//
// and then infinite recursion is occured in WriteLongFilenameHeader because truncated.Length is 102.
Name = ArchiveEncoding.Decode(
ArchiveEncoding.Encode(Name),
ArchiveEncoding.Encode(Name.NotNull("Name is null")),
0,
100 - ArchiveEncoding.GetEncoding().GetMaxByteCount(1)
);
@@ -87,7 +87,7 @@ internal sealed class TarHeader
private void WriteLongFilenameHeader(Stream output)
{
var nameBytes = ArchiveEncoding.Encode(Name);
var nameBytes = ArchiveEncoding.Encode(Name.NotNull("Name is null"));
output.Write(nameBytes, 0, nameBytes.Length);
// pad to multiple of BlockSize bytes, and make sure a terminating null is added
@@ -132,8 +132,8 @@ internal sealed class TarHeader
Mode |= 0b1_000_000_000;
}
UserId = ReadAsciiInt64Base8(buffer, 108, 7);
GroupId = ReadAsciiInt64Base8(buffer, 116, 7);
UserId = ReadAsciiInt64Base8oldGnu(buffer, 108, 7);
GroupId = ReadAsciiInt64Base8oldGnu(buffer, 116, 7);
var unixTimeStamp = ReadAsciiInt64Base8(buffer, 136, 11);
LastModifiedTime = EPOCH.AddSeconds(unixTimeStamp).ToLocalTime();
@@ -249,6 +249,24 @@ internal sealed class TarHeader
return Convert.ToInt64(s, 8);
}
private static long ReadAsciiInt64Base8oldGnu(byte[] buffer, int offset, int count)
{
if (buffer[offset] == 0x80 && buffer[offset + 1] == 0x00)
{
return buffer[offset + 4] << 24
| buffer[offset + 5] << 16
| buffer[offset + 6] << 8
| buffer[offset + 7];
}
var s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls();
if (string.IsNullOrEmpty(s))
{
return 0;
}
return Convert.ToInt64(s, 8);
}
private static long ReadAsciiInt64(byte[] buffer, int offset, int count)
{
var s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls();
@@ -305,5 +323,5 @@ internal sealed class TarHeader
public long? DataStartPosition { get; set; }
public string Magic { get; set; }
public string? Magic { get; set; }
}

View File

@@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.IO;
@@ -10,9 +8,9 @@ namespace SharpCompress.Common.Tar;
public class TarEntry : Entry
{
private readonly TarFilePart _filePart;
private readonly TarFilePart? _filePart;
internal TarEntry(TarFilePart filePart, CompressionType type)
internal TarEntry(TarFilePart? filePart, CompressionType type)
{
_filePart = filePart;
CompressionType = type;
@@ -22,15 +20,15 @@ public class TarEntry : Entry
public override long Crc => 0;
public override string Key => _filePart.Header.Name;
public override string? Key => _filePart?.Header.Name;
public override string LinkTarget => _filePart.Header.LinkName;
public override string? LinkTarget => _filePart?.Header.LinkName;
public override long CompressedSize => _filePart.Header.Size;
public override long CompressedSize => _filePart?.Header.Size ?? 0;
public override long Size => _filePart.Header.Size;
public override long Size => _filePart?.Header.Size ?? 0;
public override DateTime? LastModifiedTime => _filePart.Header.LastModifiedTime;
public override DateTime? LastModifiedTime => _filePart?.Header.LastModifiedTime;
public override DateTime? CreatedTime => null;
@@ -40,17 +38,17 @@ public class TarEntry : Entry
public override bool IsEncrypted => false;
public override bool IsDirectory => _filePart.Header.EntryType == EntryType.Directory;
public override bool IsDirectory => _filePart?.Header.EntryType == EntryType.Directory;
public override bool IsSplitAfter => false;
public long Mode => _filePart.Header.Mode;
public long Mode => _filePart?.Header.Mode ?? 0;
public long UserID => _filePart.Header.UserId;
public long UserID => _filePart?.Header.UserId ?? 0;
public long GroupId => _filePart.Header.GroupId;
public long GroupId => _filePart?.Header.GroupId ?? 0;
internal override IEnumerable<FilePart> Parts => _filePart.AsEnumerable<FilePart>();
internal override IEnumerable<FilePart> Parts => _filePart.Empty();
internal static IEnumerable<TarEntry> GetEntries(
StreamingMode mode,
@@ -59,17 +57,17 @@ public class TarEntry : Entry
ArchiveEncoding archiveEncoding
)
{
foreach (var h in TarHeaderFactory.ReadHeader(mode, stream, archiveEncoding))
foreach (var header in TarHeaderFactory.ReadHeader(mode, stream, archiveEncoding))
{
if (h != null)
if (header != null)
{
if (mode == StreamingMode.Seekable)
{
yield return new TarEntry(new TarFilePart(h, stream), compressionType);
yield return new TarEntry(new TarFilePart(header, stream), compressionType);
}
else
{
yield return new TarEntry(new TarFilePart(h, null), compressionType);
yield return new TarEntry(new TarFilePart(header, null), compressionType);
}
}
else

View File

@@ -5,9 +5,9 @@ namespace SharpCompress.Common.Tar;
internal sealed class TarFilePart : FilePart
{
private readonly Stream _seekableStream;
private readonly Stream? _seekableStream;
internal TarFilePart(TarHeader header, Stream seekableStream)
internal TarFilePart(TarHeader header, Stream? seekableStream)
: base(header.ArchiveEncoding)
{
_seekableStream = seekableStream;
@@ -16,16 +16,16 @@ internal sealed class TarFilePart : FilePart
internal TarHeader Header { get; }
internal override string FilePartName => Header.Name;
internal override string? FilePartName => Header?.Name;
internal override Stream GetCompressedStream()
{
if (_seekableStream != null)
{
_seekableStream.Position = Header.DataStartPosition!.Value;
_seekableStream.Position = Header.DataStartPosition ?? 0;
return new TarReadOnlySubStream(_seekableStream, Header.Size);
}
return Header.PackedStream;
return Header.PackedStream.NotNull();
}
internal override Stream? GetRawStream() => null;

View File

@@ -28,7 +28,6 @@ internal static class TarHeaderFactory
switch (mode)
{
case StreamingMode.Seekable:
{
header.DataStartPosition = reader.BaseStream.Position;
@@ -37,7 +36,6 @@ internal static class TarHeaderFactory
}
break;
case StreamingMode.Streaming:
{
header.PackedStream = new TarReadOnlySubStream(stream, header.Size);
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using SharpCompress.IO;
@@ -47,7 +47,7 @@ internal class TarReadOnlySubStream : NonDisposingStream
public override bool CanWrite => false;
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override long Length => throw new NotSupportedException();
@@ -63,7 +63,7 @@ internal class TarReadOnlySubStream : NonDisposingStream
{
count = (int)BytesLeftToRead;
}
int read = Stream.Read(buffer, offset, count);
var read = Stream.Read(buffer, offset, count);
if (read > 0)
{
BytesLeftToRead -= read;
@@ -78,7 +78,7 @@ internal class TarReadOnlySubStream : NonDisposingStream
{
return -1;
}
int value = Stream.ReadByte();
var value = Stream.ReadByte();
if (value != -1)
{
--BytesLeftToRead;

View File

@@ -9,11 +9,11 @@ public abstract class Volume : IVolume
{
private readonly Stream _actualStream;
internal Volume(Stream stream, ReaderOptions readerOptions, int index = 0)
internal Volume(Stream stream, ReaderOptions? readerOptions, int index = 0)
{
Index = index;
ReaderOptions = readerOptions;
if (readerOptions.LeaveStreamOpen)
ReaderOptions = readerOptions ?? new ReaderOptions();
if (ReaderOptions.LeaveStreamOpen)
{
stream = NonDisposingStream.Create(stream);
}
@@ -32,7 +32,7 @@ public abstract class Volume : IVolume
public virtual int Index { get; internal set; }
public string FileName => (_actualStream as FileStream)?.Name!;
public string? FileName => (_actualStream as FileStream)?.Name;
/// <summary>
/// RarArchive is part of a multi-part archive.

View File

@@ -14,8 +14,8 @@ internal class DirectoryEntryHeader : ZipFileEntry
VersionNeededToExtract = reader.ReadUInt16();
Flags = (HeaderFlags)reader.ReadUInt16();
CompressionMethod = (ZipCompressionMethod)reader.ReadUInt16();
LastModifiedTime = reader.ReadUInt16();
LastModifiedDate = reader.ReadUInt16();
OriginalLastModifiedTime = LastModifiedTime = reader.ReadUInt16();
OriginalLastModifiedDate = LastModifiedDate = reader.ReadUInt16();
Crc = reader.ReadUInt32();
CompressedSize = reader.ReadUInt32();
UncompressedSize = reader.ReadUInt32();
@@ -52,8 +52,8 @@ internal class DirectoryEntryHeader : ZipFileEntry
LoadExtra(extra);
var unicodePathExtra = Extra.FirstOrDefault(
u => u.Type == ExtraDataType.UnicodePathExtraField
var unicodePathExtra = Extra.FirstOrDefault(u =>
u.Type == ExtraDataType.UnicodePathExtraField
);
if (unicodePathExtra != null && ArchiveEncoding.Forced == null)
{
@@ -85,6 +85,36 @@ internal class DirectoryEntryHeader : ZipFileEntry
RelativeOffsetOfEntryHeader = zip64ExtraData.RelativeOffsetOfEntryHeader;
}
}
var unixTimeExtra = Extra.FirstOrDefault(u => u.Type == ExtraDataType.UnixTimeExtraField);
if (unixTimeExtra is not null)
{
// Tuple order is last modified time, last access time, and creation time.
var unixTimeTuple = ((UnixTimeExtraField)unixTimeExtra).UnicodeTimes;
if (unixTimeTuple.Item1.HasValue)
{
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item1.Value);
LastModifiedDate = (ushort)(dosTime >> 16);
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
}
else if (unixTimeTuple.Item2.HasValue)
{
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item2.Value);
LastModifiedDate = (ushort)(dosTime >> 16);
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
}
else if (unixTimeTuple.Item3.HasValue)
{
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item3.Value);
LastModifiedDate = (ushort)(dosTime >> 16);
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
}
}
}
internal ushort Version { get; private set; }

View File

@@ -13,8 +13,8 @@ internal class LocalEntryHeader : ZipFileEntry
Version = reader.ReadUInt16();
Flags = (HeaderFlags)reader.ReadUInt16();
CompressionMethod = (ZipCompressionMethod)reader.ReadUInt16();
LastModifiedTime = reader.ReadUInt16();
LastModifiedDate = reader.ReadUInt16();
OriginalLastModifiedTime = LastModifiedTime = reader.ReadUInt16();
OriginalLastModifiedDate = LastModifiedDate = reader.ReadUInt16();
Crc = reader.ReadUInt32();
CompressedSize = reader.ReadUInt32();
UncompressedSize = reader.ReadUInt32();
@@ -42,8 +42,8 @@ internal class LocalEntryHeader : ZipFileEntry
LoadExtra(extra);
var unicodePathExtra = Extra.FirstOrDefault(
u => u.Type == ExtraDataType.UnicodePathExtraField
var unicodePathExtra = Extra.FirstOrDefault(u =>
u.Type == ExtraDataType.UnicodePathExtraField
);
if (unicodePathExtra != null && ArchiveEncoding.Forced == null)
{
@@ -64,6 +64,36 @@ internal class LocalEntryHeader : ZipFileEntry
UncompressedSize = zip64ExtraData.UncompressedSize;
}
}
var unixTimeExtra = Extra.FirstOrDefault(u => u.Type == ExtraDataType.UnixTimeExtraField);
if (unixTimeExtra is not null)
{
// Tuple order is last modified time, last access time, and creation time.
var unixTimeTuple = ((UnixTimeExtraField)unixTimeExtra).UnicodeTimes;
if (unixTimeTuple.Item1.HasValue)
{
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item1.Value);
LastModifiedDate = (ushort)(dosTime >> 16);
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
}
else if (unixTimeTuple.Item2.HasValue)
{
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item2.Value);
LastModifiedDate = (ushort)(dosTime >> 16);
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
}
else if (unixTimeTuple.Item3.HasValue)
{
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item3.Value);
LastModifiedDate = (ushort)(dosTime >> 16);
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
}
}
}
internal ushort Version { get; private set; }

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Buffers.Binary;
using System.Text;
@@ -13,7 +13,8 @@ internal enum ExtraDataType : ushort
// Third Party Mappings
// -Info-ZIP Unicode Path Extra Field
UnicodePathExtraField = 0x7075,
Zip64ExtendedInformationExtraField = 0x0001
Zip64ExtendedInformationExtraField = 0x0001,
UnixTimeExtraField = 0x5455
}
internal class ExtraData
@@ -145,6 +146,84 @@ internal sealed class Zip64ExtendedInformationExtraField : ExtraData
public uint VolumeNumber { get; private set; }
}
internal sealed class UnixTimeExtraField : ExtraData
{
public UnixTimeExtraField(ExtraDataType type, ushort length, byte[] dataBytes)
: base(type, length, dataBytes) { }
/// <summary>
/// The unix modified time, last access time, and creation time, if set.
/// </summary>
/// <remarks>Must return Tuple explicitly due to net462 support.</remarks>
internal Tuple<DateTime?, DateTime?, DateTime?> UnicodeTimes
{
get
{
// There has to be at least 5 byte for there to be a timestamp.
// 1 byte for flags and 4 bytes for a timestamp.
if (DataBytes is null || DataBytes.Length < 5)
{
return Tuple.Create<DateTime?, DateTime?, DateTime?>(null, null, null);
}
var flags = DataBytes[0];
var isModifiedTimeSpecified = (flags & 0x01) == 1;
var isLastAccessTimeSpecified = (flags & 0x02) == 1;
var isCreationTimeSpecified = (flags & 0x04) == 1;
var currentIndex = 1;
DateTime? modifiedTime = null;
DateTime? lastAccessTime = null;
DateTime? creationTime = null;
if (isModifiedTimeSpecified)
{
var modifiedEpochTime = BinaryPrimitives.ReadInt32LittleEndian(
DataBytes.AsSpan(currentIndex, 4)
);
currentIndex += 4;
modifiedTime = DateTimeOffset.FromUnixTimeSeconds(modifiedEpochTime).UtcDateTime;
}
if (isLastAccessTimeSpecified)
{
if (currentIndex + 4 > DataBytes.Length)
{
throw new ArchiveException("Invalid UnicodeExtraTime field");
}
var lastAccessEpochTime = BinaryPrimitives.ReadInt32LittleEndian(
DataBytes.AsSpan(currentIndex, 4)
);
currentIndex += 4;
lastAccessTime = DateTimeOffset
.FromUnixTimeSeconds(lastAccessEpochTime)
.UtcDateTime;
}
if (isCreationTimeSpecified)
{
if (currentIndex + 4 > DataBytes.Length)
{
throw new ArchiveException("Invalid UnicodeExtraTime field");
}
var creationTimeEpochTime = BinaryPrimitives.ReadInt32LittleEndian(
DataBytes.AsSpan(currentIndex, 4)
);
currentIndex += 4;
creationTime = DateTimeOffset
.FromUnixTimeSeconds(creationTimeEpochTime)
.UtcDateTime;
}
return Tuple.Create(modifiedTime, lastAccessTime, creationTime);
}
}
}
internal static class LocalEntryHeaderExtraFactory
{
internal static ExtraData Create(ExtraDataType type, ushort length, byte[] extraData) =>
@@ -154,6 +233,7 @@ internal static class LocalEntryHeaderExtraFactory
=> new ExtraUnicodePathExtraField(type, length, extraData),
ExtraDataType.Zip64ExtendedInformationExtraField
=> new Zip64ExtendedInformationExtraField(type, length, extraData),
ExtraDataType.UnixTimeExtraField => new UnixTimeExtraField(type, length, extraData),
_ => new ExtraData(type, length, extraData)
};
}

View File

@@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
@@ -20,21 +18,21 @@ internal abstract class ZipFileEntry : ZipHeader
{
get
{
if (Name.EndsWith('/'))
if (Name?.EndsWith('/') ?? false)
{
return true;
}
//.NET Framework 4.5 : System.IO.Compression::CreateFromDirectory() probably writes backslashes to headers
return CompressedSize == 0 && UncompressedSize == 0 && Name.EndsWith('\\');
return CompressedSize == 0 && UncompressedSize == 0 && (Name?.EndsWith('\\') ?? false);
}
}
internal Stream PackedStream { get; set; }
internal Stream? PackedStream { get; set; }
internal ArchiveEncoding ArchiveEncoding { get; }
internal string Name { get; set; }
internal string? Name { get; set; }
internal HeaderFlags Flags { get; set; }
@@ -48,7 +46,7 @@ internal abstract class ZipFileEntry : ZipHeader
internal List<ExtraData> Extra { get; set; }
public string Password { get; set; }
public string? Password { get; set; }
internal PkwareTraditionalEncryptionData ComposeEncryptionData(Stream archiveStream)
{
@@ -65,10 +63,28 @@ internal abstract class ZipFileEntry : ZipHeader
return encryptionData;
}
internal WinzipAesEncryptionData WinzipAesEncryptionData { get; set; }
internal WinzipAesEncryptionData? WinzipAesEncryptionData { get; set; }
/// <summary>
/// The last modified date as read from the Local or Central Directory header.
/// </summary>
internal ushort OriginalLastModifiedDate { get; set; }
/// <summary>
/// The last modified date from the UnixTimeExtraField, if present, or the
/// Local or Cental Directory header, if not.
/// </summary>
internal ushort LastModifiedDate { get; set; }
/// <summary>
/// The last modified time as read from the Local or Central Directory header.
/// </summary>
internal ushort OriginalLastModifiedTime { get; set; }
/// <summary>
/// The last modified time from the UnixTimeExtraField, if present, or the
/// Local or Cental Directory header, if not.
/// </summary>
internal ushort LastModifiedTime { get; set; }
internal uint Crc { get; set; }
@@ -101,7 +117,7 @@ internal abstract class ZipFileEntry : ZipHeader
}
}
internal ZipFilePart Part { get; set; }
internal ZipFilePart? Part { get; set; }
internal bool IsZip64 => CompressedSize >= uint.MaxValue;
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
namespace SharpCompress.Common.Zip;
@@ -87,10 +87,7 @@ internal class PkwareTraditionalCryptoStream : Stream
_stream.Write(encrypted, 0, encrypted.Length);
}
public override void Flush()
{
//throw new NotSupportedException();
}
public override void Flush() { }
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();

View File

@@ -6,7 +6,7 @@ namespace SharpCompress.Common.Zip;
internal class PkwareTraditionalEncryptionData
{
private static readonly CRC32 CRC32 = new CRC32();
private static readonly CRC32 CRC32 = new();
private readonly uint[] _keys = { 0x12345678, 0x23456789, 0x34567890 };
private readonly ArchiveEncoding _archiveEncoding;
@@ -39,7 +39,7 @@ internal class PkwareTraditionalEncryptionData
{
throw new CryptographicException("The password did not match.");
}
if (plainTextHeader[11] != (byte)((header.LastModifiedTime >> 8) & 0xff))
if (plainTextHeader[11] != (byte)((header.OriginalLastModifiedTime >> 8) & 0xff))
{
throw new CryptographicException("The password did not match.");
}
@@ -103,7 +103,7 @@ internal class PkwareTraditionalEncryptionData
internal byte[] StringToByteArray(string value)
{
var a = _archiveEncoding.Password.GetBytes(value);
var a = _archiveEncoding.GetPasswordEncoding().GetBytes(value);
return a;
}

View File

@@ -42,16 +42,16 @@ internal class SeekableZipFilePart : ZipFilePart
protected override Stream CreateBaseStream()
{
BaseStream.Position = Header.DataStartPosition!.Value;
BaseStream.Position = Header.DataStartPosition.NotNull();
if (
(Header.CompressedSize == 0)
&& FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor)
&& (_directoryEntryHeader?.HasData == true)
&& (_directoryEntryHeader?.CompressedSize != 0)
&& _directoryEntryHeader.HasData
&& (_directoryEntryHeader.CompressedSize != 0)
)
{
return new ReadOnlySubStream(BaseStream, _directoryEntryHeader!.CompressedSize);
return new ReadOnlySubStream(BaseStream, _directoryEntryHeader.CompressedSize);
}
return BaseStream;

View File

@@ -13,7 +13,7 @@ internal sealed class StreamingZipFilePart : ZipFilePart
internal StreamingZipFilePart(ZipFileEntry header, Stream stream)
: base(header, stream) { }
protected override Stream CreateBaseStream() => Header.PackedStream;
protected override Stream CreateBaseStream() => Header.PackedStream.NotNull();
internal override Stream GetCompressedStream()
{

View File

@@ -15,10 +15,7 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory
ArchiveEncoding archiveEncoding,
IEnumerable<ZipEntry>? entries
)
: base(StreamingMode.Streaming, password, archiveEncoding)
{
_entries = entries;
}
: base(StreamingMode.Streaming, password, archiveEncoding) => _entries = entries;
internal IEnumerable<ZipHeader> ReadStreamHeader(Stream stream)
{
@@ -45,6 +42,10 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory
)
)
{
if (_lastEntryHeader.Part is null)
{
continue;
}
reader = ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation(
ref rewindableStream
);
@@ -97,13 +98,12 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory
if (header.ZipHeaderType == ZipHeaderType.LocalEntry)
{
var local_header = ((LocalEntryHeader)header);
var dir_header = _entries?.FirstOrDefault(
entry =>
entry.Key == local_header.Name
&& local_header.CompressedSize == 0
&& local_header.UncompressedSize == 0
&& local_header.Crc == 0
&& local_header.IsDirectory == false
var dir_header = _entries?.FirstOrDefault(entry =>
entry.Key == local_header.Name
&& local_header.CompressedSize == 0
&& local_header.UncompressedSize == 0
&& local_header.Crc == 0
&& local_header.IsDirectory == false
);
if (dir_header != null)

View File

@@ -73,7 +73,7 @@ internal class WinzipAesCryptoStream : Stream
}
}
public override void Flush() => throw new NotSupportedException();
public override void Flush() { }
public override int Read(byte[] buffer, int offset, int count)
{

View File

@@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Buffers.Binary;
using System.Security.Cryptography;
@@ -10,12 +8,7 @@ internal class WinzipAesEncryptionData
{
private const int RFC2898_ITERATIONS = 1000;
private readonly byte[] _salt;
private readonly WinzipAesKeySize _keySize;
private readonly byte[] _passwordVerifyValue;
private readonly string _password;
private byte[] _generatedVerifyValue;
internal WinzipAesEncryptionData(
WinzipAesKeySize keySize,
@@ -25,10 +18,28 @@ internal class WinzipAesEncryptionData
)
{
_keySize = keySize;
_salt = salt;
_passwordVerifyValue = passwordVerifyValue;
_password = password;
Initialize();
#if NETFRAMEWORK || NETSTANDARD2_0
var rfc2898 = new Rfc2898DeriveBytes(password, salt, RFC2898_ITERATIONS);
#else
var rfc2898 = new Rfc2898DeriveBytes(
password,
salt,
RFC2898_ITERATIONS,
HashAlgorithmName.SHA1
);
#endif
KeyBytes = rfc2898.GetBytes(KeySizeInBytes); // 16 or 24 or 32 ???
IvBytes = rfc2898.GetBytes(KeySizeInBytes);
var generatedVerifyValue = rfc2898.GetBytes(2);
var verify = BinaryPrimitives.ReadInt16LittleEndian(passwordVerifyValue);
var generated = BinaryPrimitives.ReadInt16LittleEndian(generatedVerifyValue);
if (verify != generated)
{
throw new InvalidFormatException("bad password");
}
}
internal byte[] IvBytes { get; set; }
@@ -45,32 +56,4 @@ internal class WinzipAesEncryptionData
WinzipAesKeySize.KeySize256 => 32,
_ => throw new InvalidOperationException(),
};
private void Initialize()
{
#if NET7_0
var rfc2898 = new Rfc2898DeriveBytes(
_password,
_salt,
RFC2898_ITERATIONS,
HashAlgorithmName.SHA1
);
#else
var rfc2898 = new Rfc2898DeriveBytes(_password, _salt, RFC2898_ITERATIONS);
#endif
KeyBytes = rfc2898.GetBytes(KeySizeInBytes); // 16 or 24 or 32 ???
IvBytes = rfc2898.GetBytes(KeySizeInBytes);
_generatedVerifyValue = rfc2898.GetBytes(2);
var verify = BinaryPrimitives.ReadInt16LittleEndian(_passwordVerifyValue);
if (_password != null)
{
var generated = BinaryPrimitives.ReadInt16LittleEndian(_generatedVerifyValue);
if (verify != generated)
{
throw new InvalidFormatException("bad password");
}
}
}
}

View File

@@ -3,6 +3,7 @@ namespace SharpCompress.Common.Zip;
internal enum ZipCompressionMethod
{
None = 0,
Shrink = 1,
Deflate = 8,
Deflate64 = 9,
BZip2 = 12,

View File

@@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using SharpCompress.Common.Zip.Headers;
@@ -8,67 +6,43 @@ namespace SharpCompress.Common.Zip;
public class ZipEntry : Entry
{
private readonly ZipFilePart _filePart;
private readonly ZipFilePart? _filePart;
internal ZipEntry(ZipFilePart filePart)
internal ZipEntry(ZipFilePart? filePart)
{
if (filePart != null)
if (filePart == null)
{
_filePart = filePart;
LastModifiedTime = Utility.DosDateToDateTime(
filePart.Header.LastModifiedDate,
filePart.Header.LastModifiedTime
);
return;
}
_filePart = filePart;
LastModifiedTime = Utility.DosDateToDateTime(
filePart.Header.LastModifiedDate,
filePart.Header.LastModifiedTime
);
}
public override CompressionType CompressionType
{
get
public override CompressionType CompressionType =>
_filePart?.Header.CompressionMethod switch
{
switch (_filePart.Header.CompressionMethod)
{
case ZipCompressionMethod.BZip2:
{
return CompressionType.BZip2;
}
case ZipCompressionMethod.Deflate:
{
return CompressionType.Deflate;
}
case ZipCompressionMethod.Deflate64:
{
return CompressionType.Deflate64;
}
case ZipCompressionMethod.LZMA:
{
return CompressionType.LZMA;
}
case ZipCompressionMethod.PPMd:
{
return CompressionType.PPMd;
}
case ZipCompressionMethod.None:
{
return CompressionType.None;
}
default:
{
return CompressionType.Unknown;
}
}
}
}
ZipCompressionMethod.BZip2 => CompressionType.BZip2,
ZipCompressionMethod.Deflate => CompressionType.Deflate,
ZipCompressionMethod.Deflate64 => CompressionType.Deflate64,
ZipCompressionMethod.LZMA => CompressionType.LZMA,
ZipCompressionMethod.PPMd => CompressionType.PPMd,
ZipCompressionMethod.None => CompressionType.None,
ZipCompressionMethod.Shrink => CompressionType.Shrink,
_ => CompressionType.Unknown
};
public override long Crc => _filePart.Header.Crc;
public override long Crc => _filePart?.Header.Crc ?? 0;
public override string Key => _filePart.Header.Name;
public override string? Key => _filePart?.Header.Name;
public override string LinkTarget => null;
public override string? LinkTarget => null;
public override long CompressedSize => _filePart.Header.CompressedSize;
public override long CompressedSize => _filePart?.Header.CompressedSize ?? 0;
public override long Size => _filePart.Header.UncompressedSize;
public override long Size => _filePart?.Header.UncompressedSize ?? 0;
public override DateTime? LastModifiedTime { get; }
@@ -79,11 +53,11 @@ public class ZipEntry : Entry
public override DateTime? ArchivedTime => null;
public override bool IsEncrypted =>
FlagUtility.HasFlag(_filePart.Header.Flags, HeaderFlags.Encrypted);
FlagUtility.HasFlag(_filePart?.Header.Flags ?? HeaderFlags.None, HeaderFlags.Encrypted);
public override bool IsDirectory => _filePart.Header.IsDirectory;
public override bool IsDirectory => _filePart?.Header.IsDirectory ?? false;
public override bool IsSplitAfter => false;
internal override IEnumerable<FilePart> Parts => _filePart.AsEnumerable<FilePart>();
internal override IEnumerable<FilePart> Parts => _filePart.Empty();
}

View File

@@ -9,6 +9,7 @@ using SharpCompress.Compressors.Deflate;
using SharpCompress.Compressors.Deflate64;
using SharpCompress.Compressors.LZMA;
using SharpCompress.Compressors.PPMd;
using SharpCompress.Compressors.Shrink;
using SharpCompress.Compressors.Xz;
using SharpCompress.IO;
using ZstdSharp;
@@ -28,7 +29,7 @@ internal abstract class ZipFilePart : FilePart
internal Stream BaseStream { get; }
internal ZipFileEntry Header { get; set; }
internal override string FilePartName => Header.Name;
internal override string? FilePartName => Header.Name;
internal override Stream GetCompressedStream()
{
@@ -79,6 +80,15 @@ internal abstract class ZipFilePart : FilePart
return new DataDescriptorStream(stream);
}
case ZipCompressionMethod.Shrink:
{
return new ShrinkStream(
stream,
CompressionMode.Decompress,
Header.CompressedSize,
Header.UncompressedSize
);
}
case ZipCompressionMethod.Deflate:
{
return new DeflateStream(stream, CompressionMode.Decompress);
@@ -192,6 +202,7 @@ internal abstract class ZipFilePart : FilePart
switch (Header.CompressionMethod)
{
case ZipCompressionMethod.None:
case ZipCompressionMethod.Shrink:
case ZipCompressionMethod.Deflate:
case ZipCompressionMethod.Deflate64:
case ZipCompressionMethod.BZip2:

View File

@@ -55,7 +55,13 @@ internal class ZipHeaderFactory
}
case POST_DATA_DESCRIPTOR:
{
if (FlagUtility.HasFlag(_lastEntryHeader!.Flags, HeaderFlags.UsePostDataDescriptor))
if (
FlagUtility.HasFlag(
_lastEntryHeader.NotNull().Flags,
HeaderFlags.UsePostDataDescriptor
)
&& _lastEntryHeader != null
)
{
_lastEntryHeader.Crc = reader.ReadUInt32();
_lastEntryHeader.CompressedSize = zip64
@@ -142,9 +148,9 @@ internal class ZipHeaderFactory
if (entryHeader.CompressionMethod == ZipCompressionMethod.WinzipAes)
{
var data = entryHeader
.Extra
.SingleOrDefault(x => x.Type == ExtraDataType.WinZipAes);
var data = entryHeader.Extra.SingleOrDefault(x =>
x.Type == ExtraDataType.WinZipAes
);
if (data != null)
{
var keySize = (WinzipAesKeySize)data.DataBytes[4];

View File

@@ -69,7 +69,7 @@ public sealed class BZip2Stream : Stream
public override void SetLength(long value) => stream.SetLength(value);
#if !NETFRAMEWORK && !NETSTANDARD2_0
#if !NETFRAMEWORK&& !NETSTANDARD2_0
public override int Read(Span<byte> buffer) => stream.Read(buffer);

View File

@@ -87,7 +87,7 @@ internal class CBZip2InputStream : Stream
private int bsBuff;
private int bsLive;
private readonly CRC mCrc = new CRC();
private readonly CRC mCrc = new();
private readonly bool[] inUse = new bool[256];
private int nInUse;

View File

@@ -284,7 +284,7 @@ internal sealed class CBZip2OutputStream : Stream
private int bytesOut;
private int bsBuff;
private int bsLive;
private readonly CRC mCrc = new CRC();
private readonly CRC mCrc = new();
private readonly bool[] inUse = new bool[256];
private int nInUse;

View File

@@ -342,9 +342,9 @@ internal sealed partial class DeflateManager
private readonly short[] dyn_dtree; // distance tree
private readonly short[] bl_tree; // Huffman tree for bit lengths
private readonly Tree treeLiterals = new Tree(); // desc for literal tree
private readonly Tree treeDistances = new Tree(); // desc for distance tree
private readonly Tree treeBitLengths = new Tree(); // desc for bit length tree
private readonly Tree treeLiterals = new(); // desc for literal tree
private readonly Tree treeDistances = new(); // desc for distance tree
private readonly Tree treeBitLengths = new(); // desc for bit length tree
// number of codes at each bit length for an optimal tree
private readonly short[] bl_count = new short[InternalConstants.MAX_BITS + 1];
@@ -1787,21 +1787,14 @@ internal sealed partial class DeflateManager
return status == BUSY_STATE ? ZlibConstants.Z_DATA_ERROR : ZlibConstants.Z_OK;
}
private void SetDeflater()
{
switch (config.Flavor)
private void SetDeflater() =>
DeflateFunction = config.Flavor switch
{
case DeflateFlavor.Store:
DeflateFunction = DeflateNone;
break;
case DeflateFlavor.Fast:
DeflateFunction = DeflateFast;
break;
case DeflateFlavor.Slow:
DeflateFunction = DeflateSlow;
break;
}
}
DeflateFlavor.Store => DeflateNone,
DeflateFlavor.Fast => DeflateFast,
DeflateFlavor.Slow => DeflateSlow,
_ => DeflateFunction
};
internal int SetParams(CompressionLevel level, CompressionStrategy strategy)
{

View File

@@ -366,9 +366,5 @@ public class DeflateStream : Stream
#endregion
public MemoryStream InputBuffer =>
new MemoryStream(
_baseStream._z.InputBuffer,
_baseStream._z.NextIn,
_baseStream._z.AvailableBytesIn
);
new(_baseStream._z.InputBuffer, _baseStream._z.NextIn, _baseStream._z.AvailableBytesIn);
}

View File

@@ -35,15 +35,7 @@ namespace SharpCompress.Compressors.Deflate;
public class GZipStream : Stream
{
internal static readonly DateTime UNIX_EPOCH = new DateTime(
1970,
1,
1,
0,
0,
0,
DateTimeKind.Utc
);
internal static readonly DateTime UNIX_EPOCH = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
private string? _comment;
private string? _fileName;

View File

@@ -105,11 +105,11 @@ internal sealed class InflateBlocks
internal int[] blens; // bit lengths of codes
internal uint check; // check on output
internal object checkfn; // check function
internal InflateCodes codes = new InflateCodes(); // if CODES, current state
internal InflateCodes codes = new(); // if CODES, current state
internal int end; // one byte after sliding window
internal int[] hufts; // single malloc for tree space
internal int index; // index into blens (or border)
internal InfTree inftree = new InfTree();
internal InfTree inftree = new();
internal int last; // true if this block is the last block
internal int left; // if STORED, bytes left to copy
private InflateBlockMode mode; // current inflate_block mode

View File

@@ -102,7 +102,7 @@ internal class ZlibBaseStream : Stream
{
if (_z is null)
{
bool wantRfc1950Header = (_flavor == ZlibStreamFlavor.ZLIB);
var wantRfc1950Header = (_flavor == ZlibStreamFlavor.ZLIB);
_z = new ZlibCodec();
if (_compressionMode == CompressionMode.Decompress)
{
@@ -147,13 +147,13 @@ internal class ZlibBaseStream : Stream
z.InputBuffer = buffer;
_z.NextIn = offset;
_z.AvailableBytesIn = count;
bool done = false;
var done = false;
do
{
_z.OutputBuffer = workingBuffer;
_z.NextOut = 0;
_z.AvailableBytesOut = _workingBuffer.Length;
int rc = (_wantCompress) ? _z.Deflate(_flushMode) : _z.Inflate(_flushMode);
var rc = (_wantCompress) ? _z.Deflate(_flushMode) : _z.Inflate(_flushMode);
if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END)
{
throw new ZlibException((_wantCompress ? "de" : "in") + "flating: " + _z.Message);
@@ -181,18 +181,18 @@ internal class ZlibBaseStream : Stream
if (_streamMode == StreamMode.Writer)
{
bool done = false;
var done = false;
do
{
_z.OutputBuffer = workingBuffer;
_z.NextOut = 0;
_z.AvailableBytesOut = _workingBuffer.Length;
int rc =
var rc =
(_wantCompress) ? _z.Deflate(FlushType.Finish) : _z.Inflate(FlushType.Finish);
if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK)
{
string verb = (_wantCompress ? "de" : "in") + "flating";
var verb = (_wantCompress ? "de" : "in") + "flating";
if (_z.Message is null)
{
throw new ZlibException(String.Format("{0}: (rc = {1})", verb, rc));
@@ -225,7 +225,7 @@ internal class ZlibBaseStream : Stream
Span<byte> intBuf = stackalloc byte[4];
BinaryPrimitives.WriteInt32LittleEndian(intBuf, crc.Crc32Result);
_stream.Write(intBuf);
int c2 = (int)(crc.TotalBytesRead & 0x00000000FFFFFFFF);
var c2 = (int)(crc.TotalBytesRead & 0x00000000FFFFFFFF);
BinaryPrimitives.WriteInt32LittleEndian(intBuf, c2);
_stream.Write(intBuf);
}
@@ -256,8 +256,8 @@ internal class ZlibBaseStream : Stream
{
// Make sure we have read to the end of the stream
_z.InputBuffer.AsSpan(_z.NextIn, _z.AvailableBytesIn).CopyTo(trailer);
int bytesNeeded = 8 - _z.AvailableBytesIn;
int bytesRead = _stream.Read(
var bytesNeeded = 8 - _z.AvailableBytesIn;
var bytesRead = _stream.Read(
trailer.Slice(_z.AvailableBytesIn, bytesNeeded)
);
if (bytesNeeded != bytesRead)
@@ -275,10 +275,10 @@ internal class ZlibBaseStream : Stream
_z.InputBuffer.AsSpan(_z.NextIn, trailer.Length).CopyTo(trailer);
}
Int32 crc32_expected = BinaryPrimitives.ReadInt32LittleEndian(trailer);
Int32 crc32_actual = crc.Crc32Result;
Int32 isize_expected = BinaryPrimitives.ReadInt32LittleEndian(trailer.Slice(4));
Int32 isize_actual = (Int32)(_z.TotalBytesOut & 0x00000000FFFFFFFF);
var crc32_expected = BinaryPrimitives.ReadInt32LittleEndian(trailer);
var crc32_actual = crc.Crc32Result;
var isize_expected = BinaryPrimitives.ReadInt32LittleEndian(trailer.Slice(4));
var isize_actual = (Int32)(_z.TotalBytesOut & 0x00000000FFFFFFFF);
if (crc32_actual != crc32_expected)
{
@@ -380,11 +380,11 @@ internal class ZlibBaseStream : Stream
private string ReadZeroTerminatedString()
{
var list = new List<byte>();
bool done = false;
var done = false;
do
{
// workitem 7740
int n = _stream.Read(_buf1, 0, 1);
var n = _stream.Read(_buf1, 0, 1);
if (n != 1)
{
throw new ZlibException("Unexpected EOF reading GZIP header.");
@@ -398,17 +398,17 @@ internal class ZlibBaseStream : Stream
list.Add(_buf1[0]);
}
} while (!done);
byte[] buffer = list.ToArray();
var buffer = list.ToArray();
return _encoding.GetString(buffer, 0, buffer.Length);
}
private int _ReadAndValidateGzipHeader()
{
int totalBytesRead = 0;
var totalBytesRead = 0;
// read the header on the first read
Span<byte> header = stackalloc byte[10];
int n = _stream.Read(header);
var n = _stream.Read(header);
// workitem 8501: handle edge case (decompress empty stream)
if (n == 0)
@@ -426,7 +426,7 @@ internal class ZlibBaseStream : Stream
throw new ZlibException("Bad GZIP header.");
}
int timet = BinaryPrimitives.ReadInt32LittleEndian(header.Slice(4));
var timet = BinaryPrimitives.ReadInt32LittleEndian(header.Slice(4));
_GzipMtime = TarHeader.EPOCH.AddSeconds(timet);
totalBytesRead += n;
if ((header[3] & 0x04) == 0x04)
@@ -435,8 +435,8 @@ internal class ZlibBaseStream : Stream
n = _stream.Read(header.Slice(0, 2)); // 2-byte length field
totalBytesRead += n;
short extraLength = (short)(header[0] + header[1] * 256);
byte[] extra = new byte[extraLength];
var extraLength = (short)(header[0] + header[1] * 256);
var extra = new byte[extraLength];
n = _stream.Read(extra, 0, extra.Length);
if (n != extraLength)
{
@@ -498,7 +498,7 @@ internal class ZlibBaseStream : Stream
throw new ZlibException("Cannot Read after Writing.");
}
int rc = 0;
var rc = 0;
// set up the output of the deflate/inflate codec:
_z.OutputBuffer = buffer;

View File

@@ -118,8 +118,8 @@ public sealed class Deflate64Stream : Stream
EnsureNotDisposed();
int bytesRead;
int currentOffset = offset;
int remainingCount = count;
var currentOffset = offset;
var remainingCount = count;
while (true)
{
@@ -142,7 +142,7 @@ public sealed class Deflate64Stream : Stream
break;
}
int bytes = _stream.Read(_buffer, 0, _buffer.Length);
var bytes = _stream.Read(_buffer, 0, _buffer.Length);
if (bytes <= 0)
{
break;

View File

@@ -22,7 +22,7 @@ internal sealed class DeflateInput
Debug.Assert(StartIndex + Count <= Buffer.Length, "Input buffer is in invalid state!");
}
internal InputState DumpState() => new InputState(Count, StartIndex);
internal InputState DumpState() => new(Count, StartIndex);
internal void RestoreState(InputState state)
{

View File

@@ -42,11 +42,9 @@ internal sealed class HuffmanTree
private readonly int _tableMask;
// huffman tree for static block
public static HuffmanTree StaticLiteralLengthTree { get; } =
new HuffmanTree(GetStaticLiteralTreeLength());
public static HuffmanTree StaticLiteralLengthTree { get; } = new(GetStaticLiteralTreeLength());
public static HuffmanTree StaticDistanceTree { get; } =
new HuffmanTree(GetStaticDistanceTreeLength());
public static HuffmanTree StaticDistanceTree { get; } = new(GetStaticDistanceTreeLength());
public HuffmanTree(byte[] codeLengths)
{

Some files were not shown because too many files have changed in this diff Show More