Compare commits

...

26 Commits

Author SHA1 Message Date
Adam Hathcock
b3a4fed8be Mark for 0.15.2 2017-03-09 11:02:44 +00:00
Adam Hathcock
d0b4af6666 Merge pull request #210 from kenkendk/fix_invalid_headers
Fix invalid headers
2017-03-09 10:41:18 +00:00
Kenneth Skovhede
81ab5c189d Fixed writing correct headers in zip archives 2017-03-09 11:34:24 +01:00
Kenneth Skovhede
6ef3be4b5c Fixed writing correct headers in zip archives 2017-03-09 11:32:20 +01:00
Adam Hathcock
9f90a1d651 Mark for 0.15.1 2017-01-25 09:31:01 +00:00
Adam Hathcock
ce9a3fd1ef Add file ordering fix for OS X 2017-01-25 09:29:13 +00:00
Adam Hathcock
7c6f05058e Merge pull request #206 from markryd/zip64-extraction
Zip64 extending information and ZipReader
2017-01-25 09:03:43 +00:00
Mark Rydstrom
a8c3a7439e Add support for zip64 to ZipReader 2017-01-25 17:05:48 +10:00
Mark Rydstrom
839b3ab0cf Add support for zip64 extended information field 2017-01-25 16:51:15 +10:00
Adam Hathcock
44d54db80e Fix some path issues on OS X when running tests. 2017-01-24 17:36:51 +00:00
Adam Hathcock
a67d7bc429 Mark for 0.15 2017-01-24 17:25:19 +00:00
Adam Hathcock
079a818c6c Merge pull request #205 from markryd/zip64-extraction
Add zip64 support for ZipArchive extraction
2017-01-24 16:56:42 +00:00
Mark Rydstrom
6be6ef0b5c Add zip64 support for ZipArchive extraction 2017-01-24 13:04:03 +10:00
Adam Hathcock
8e51d9d646 0.14.1 2016-11-30 14:26:18 +00:00
Adam Hathcock
ea206f4f02 Merge pull request #199 from adamhathcock/Issue-198
Gzip entry can't be read multiple times
2016-11-25 09:33:56 +00:00
Adam Hathcock
f175a2a252 Merge branch 'master' into Issue-198 2016-11-25 09:21:44 +00:00
Adam Hathcock
3f7e559b86 Merge pull request #200 from ITnent/bug/Issue-197
Open branch, to fix multiple crashes on repeated zip archives reading…
2016-11-25 09:21:34 +00:00
Vladimir Demidov
2959b4d701 Modified check integrity condition for the encrypted file. 2016-11-24 20:41:08 +03:00
Vladimir Demidov
031286c5eb Fixed defects after review. 2016-11-24 18:01:49 +03:00
Vladimir Demidov
e181fa8c4a Restored original tabs. 2016-11-24 17:11:43 +03:00
Vladimir Demidov
7b035bec5d Fixed some issues after review. 2016-11-24 16:21:02 +03:00
Vladimir Demidov
f39d2bf53a Open branch, to fix multiple crashes on repeated zip archives reading. Added fix. 2016-11-24 15:14:29 +03:00
Adam Hathcock
7c8e407182 Merge branch 'master' into Issue-198 2016-11-21 12:21:29 +00:00
Adam Hathcock
a09136d46b Merge pull request #195 from jskeet/strong-naming
Strong-name both the main and test projects
2016-11-21 12:06:13 +00:00
Adam Hathcock
5fe1363ee1 Gzip entry can't be read multiple times https://github.com/adamhathcock/sharpcompress/issues/198 2016-11-21 12:04:35 +00:00
Jon Skeet
b41823fc10 Strong-name both the main and test projects
It's not clear whether SharpCompress.Test.Portable (as referenced
in AssemblyInfo.cs) still exists, but build.ps1 certainly works.
2016-11-15 18:42:56 +00:00
29 changed files with 559 additions and 151 deletions

View File

@@ -1,11 +1,13 @@
# Archive Formats
## Accessing Archives
Archive classes allow random access to a seekable stream.
Reader classes allow forward-only reading
Writer classes allow forward-only Writing
## Supported Format Table
| Archive Format | Compression Format(s) | Compress/Decompress | Archive API | Reader API | Writer API |
| --- | --- | --- | --- | --- | --- |
| Rar | Rar | Decompress (1) | RarArchive | RarReader | N/A |
@@ -15,11 +17,12 @@ Writer classes allow forward-only Writing
| 7Zip (4) | LZMA, LZMA2, BZip2, PPMd, BCJ, BCJ2, Deflate | Decompress | SevenZipArchive | N/A | N/A |
1. SOLID Rars are only supported in the RarReader API.
2. Zip format supports pkware and WinzipAES encryption. However, encrypted LZMA is not supported.
2. Zip format supports pkware and WinzipAES encryption. However, encrypted LZMA is not supported. Zip64 reading is supported.
3. The Tar format requires a file size in the header. If no size is specified to the TarWriter and the stream is not seekable, then an exception will be thrown.
4. The 7Zip format doesn't allow for reading as a forward-only stream so 7Zip is only supported through the Archive API
## Compressors
For those who want to directly compress/decompress bits
| Compressor | Compress/Decompress |

View File

@@ -25,12 +25,30 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un
* RAR 5 support
* 7Zip writing
* Zip64
* Zip64 (Need writing and extend Reading)
* Multi-volume Zip support.
* RAR5 support
## Version Log
### Version 0.15.2
* [Fix invalid headers](https://github.com/adamhathcock/sharpcompress/pull/210) - fixes an issue creating large-ish zip archives that was introduced with zip64 reading.
### Version 0.15.1
* [Zip64 extending information and ZipReader](https://github.com/adamhathcock/sharpcompress/pull/206)
### Version 0.15.0
* [Add zip64 support for ZipArchive extraction](https://github.com/adamhathcock/sharpcompress/pull/205)
### Version 0.14.1
* [.NET Assemblies aren't strong named](https://github.com/adamhathcock/sharpcompress/issues/158)
* [Pkware encryption for Zip files didn't allow for multiple reads of an entry](https://github.com/adamhathcock/sharpcompress/issues/197)
* [GZip Entry couldn't be read multiple times](https://github.com/adamhathcock/sharpcompress/issues/198)
### Version 0.14.0
* [Support for LZip reading in for Tars](https://github.com/adamhathcock/sharpcompress/pull/191)
@@ -117,8 +135,6 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un
* Embedded some BouncyCastle crypto classes to allow RAR Decryption and Winzip AES Decryption in Portable and Windows Store DLLs
* Built in Release (I think)
Some Help/Discussion: https://sharpcompress.codeplex.com/discussions
7Zip implementation based on: https://code.google.com/p/managed-lzma/
LICENSE

View File

@@ -1,44 +1,44 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24720.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F18F1765-4A02-42FD-9BEF-F0E2FCBD9D17}"
ProjectSection(SolutionItems) = preProject
global.json = global.json
EndProjectSection
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SharpCompress", "src\SharpCompress\SharpCompress.xproj", "{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3C5BE746-03E5-4895-9988-0B57F162F86C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0F0901FF-E8D9-426A-B5A2-17C7F47C1529}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SharpCompress.Test", "test\SharpCompress.Test\SharpCompress.Test.xproj", "{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}"
ProjectSection(ProjectDependencies) = postProject
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998} = {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Release|Any CPU.Build.0 = Release|Any CPU
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998} = {3C5BE746-03E5-4895-9988-0B57F162F86C}
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5} = {0F0901FF-E8D9-426A-B5A2-17C7F47C1529}
EndGlobalSection
EndGlobal
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24720.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F18F1765-4A02-42FD-9BEF-F0E2FCBD9D17}"
ProjectSection(SolutionItems) = preProject
global.json = global.json
EndProjectSection
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SharpCompress", "src\SharpCompress\SharpCompress.xproj", "{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3C5BE746-03E5-4895-9988-0B57F162F86C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0F0901FF-E8D9-426A-B5A2-17C7F47C1529}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SharpCompress.Test", "test\SharpCompress.Test\SharpCompress.Test.xproj", "{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}"
ProjectSection(ProjectDependencies) = postProject
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998} = {FD19DDD8-72B2-4024-8665-0D1F7A2AA998}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998}.Release|Any CPU.Build.0 = Release|Any CPU
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{FD19DDD8-72B2-4024-8665-0D1F7A2AA998} = {3C5BE746-03E5-4895-9988-0B57F162F86C}
{3B80E585-A2F3-4666-8F69-C7FFDA0DD7E5} = {0F0901FF-E8D9-426A-B5A2-17C7F47C1529}
EndGlobalSection
EndGlobal

BIN
SharpCompress.snk Normal file

Binary file not shown.

View File

@@ -1,4 +1,4 @@
version: '0.13.{build}'
version: '0.15.{build}'
init:
- git config --global core.autocrlf true

View File

@@ -14,6 +14,12 @@ namespace SharpCompress.Archives.GZip
public virtual Stream OpenEntryStream()
{
//this is to reset the stream to be read multiple times
var part = Parts.Single() as GZipFilePart;
if (part.GetRawStream().Position != part.EntryStartPosition)
{
part.GetRawStream().Position = part.EntryStartPosition;
}
return Parts.Single().GetCompressedStream();
}

View File

@@ -4,6 +4,22 @@ using System.Runtime.CompilerServices;
[assembly: AssemblyTitle("SharpCompress")]
[assembly: AssemblyProduct("SharpCompress")]
[assembly: InternalsVisibleTo("SharpCompress.Test")]
[assembly: InternalsVisibleTo("SharpCompress.Test.Portable")]
[assembly: CLSCompliant(true)]
[assembly: InternalsVisibleTo("SharpCompress.Test" + SharpCompress.AssemblyInfo.PublicKeySuffix)]
[assembly: InternalsVisibleTo("SharpCompress.Test.Portable" + SharpCompress.AssemblyInfo.PublicKeySuffix)]
[assembly: CLSCompliant(true)]
namespace SharpCompress
{
/// <summary>
/// Just a static class to house the public key, to avoid repetition.
/// </summary>
internal static class AssemblyInfo
{
internal const string PublicKeySuffix =
",PublicKey=002400000480000094000000060200000024000052534131000400000100010059acfa17d26c44" +
"7a4d03f16eaa72c9187c04f16e6569dd168b080e39a6f5c9fd00f28c768cd8e9a089d5a0e1b34c" +
"cd971488e7afe030ce5ce8df2053cf12ec89f6d38065c434c09ee6af3ee284c5dc08f44774b679" +
"bf39298e57efe30d4b00aecf9e4f6f8448b2cb0146d8956dfcab606cc64a0ac38c60a7d78b0d65" +
"d3b98dc0";
}
}

View File

@@ -16,9 +16,12 @@ namespace SharpCompress.Common.GZip
internal GZipFilePart(Stream stream)
{
ReadAndValidateGzipHeader(stream);
EntryStartPosition = stream.Position;
this.stream = stream;
}
internal long EntryStartPosition { get; }
internal DateTime? DateModified { get; private set; }
internal override string FilePartName { get { return name; } }

View File

@@ -48,5 +48,15 @@ namespace SharpCompress.Common.Zip.Headers
public byte[] Comment { get; private set; }
public ushort TotalNumberOfEntries { get; private set; }
public bool IsZip64
{
get
{
return TotalNumberOfEntriesInDisk == ushort.MaxValue
|| DirectorySize == uint.MaxValue
|| DirectoryStartOffsetRelativeToDisk == uint.MaxValue;
}
}
}
}

View File

@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Linq;
namespace SharpCompress.Common.Zip.Headers
@@ -41,6 +42,23 @@ namespace SharpCompress.Common.Zip.Headers
{
Name = ((ExtraUnicodePathExtraField)unicodePathExtra).UnicodeName;
}
var zip64ExtraData = Extra.OfType<Zip64ExtendedInformationExtraField>().FirstOrDefault();
if (zip64ExtraData != null)
{
if (CompressedSize == uint.MaxValue)
{
CompressedSize = zip64ExtraData.CompressedSize;
}
if (UncompressedSize == uint.MaxValue)
{
UncompressedSize = zip64ExtraData.UncompressedSize;
}
if (RelativeOffsetOfEntryHeader == uint.MaxValue)
{
RelativeOffsetOfEntryHeader = zip64ExtraData.RelativeOffsetOfEntryHeader;
}
}
}
internal override void Write(BinaryWriter writer)
@@ -52,8 +70,8 @@ namespace SharpCompress.Common.Zip.Headers
writer.Write(LastModifiedTime);
writer.Write(LastModifiedDate);
writer.Write(Crc);
writer.Write(CompressedSize);
writer.Write(UncompressedSize);
writer.Write((uint)CompressedSize);
writer.Write((uint)UncompressedSize);
byte[] nameBytes = EncodeString(Name);
writer.Write((ushort)nameBytes.Length);
@@ -77,7 +95,7 @@ namespace SharpCompress.Common.Zip.Headers
public ushort VersionNeededToExtract { get; set; }
public uint RelativeOffsetOfEntryHeader { get; set; }
public long RelativeOffsetOfEntryHeader { get; set; }
public uint ExternalFileAttributes { get; set; }

View File

@@ -32,6 +32,19 @@ namespace SharpCompress.Common.Zip.Headers
{
Name = ((ExtraUnicodePathExtraField)unicodePathExtra).UnicodeName;
}
var zip64ExtraData = Extra.OfType<Zip64ExtendedInformationExtraField>().FirstOrDefault();
if (zip64ExtraData != null)
{
if (CompressedSize == uint.MaxValue)
{
CompressedSize = zip64ExtraData.CompressedSize;
}
if (UncompressedSize == uint.MaxValue)
{
UncompressedSize = zip64ExtraData.UncompressedSize;
}
}
}
internal override void Write(BinaryWriter writer)
@@ -42,8 +55,8 @@ namespace SharpCompress.Common.Zip.Headers
writer.Write(LastModifiedTime);
writer.Write(LastModifiedDate);
writer.Write(Crc);
writer.Write(CompressedSize);
writer.Write(UncompressedSize);
writer.Write((uint)CompressedSize);
writer.Write((uint)UncompressedSize);
byte[] nameBytes = EncodeString(Name);

View File

@@ -1,5 +1,6 @@
using System;
using System.Text;
using SharpCompress.Converters;
namespace SharpCompress.Common.Zip.Headers
{
@@ -11,7 +12,8 @@ namespace SharpCompress.Common.Zip.Headers
// Third Party Mappings
// -Info-ZIP Unicode Path Extra Field
UnicodePathExtraField = 0x7075
UnicodePathExtraField = 0x7075,
Zip64ExtendedInformationExtraField = 0x0001
}
internal class ExtraData
@@ -47,6 +49,73 @@ namespace SharpCompress.Common.Zip.Headers
}
}
internal class Zip64ExtendedInformationExtraField : ExtraData
{
public Zip64ExtendedInformationExtraField(ExtraDataType type, ushort length, byte[] dataBytes)
{
Type = type;
Length = length;
DataBytes = dataBytes;
Process();
}
//From the spec values are only in the extradata if the standard
//value is set to 0xFFFF, but if one of the sizes are present, both are.
//Hence if length == 4 volume only
// if length == 8 offset only
// if length == 12 offset + volume
// if length == 16 sizes only
// if length == 20 sizes + volume
// if length == 24 sizes + offset
// if length == 28 everything.
//It is unclear how many of these are used in the wild.
private void Process()
{
switch (DataBytes.Length)
{
case 4:
VolumeNumber = DataConverter.LittleEndian.GetUInt32(DataBytes, 0);
return;
case 8:
RelativeOffsetOfEntryHeader = (long)DataConverter.LittleEndian.GetUInt64(DataBytes, 0);
return;
case 12:
RelativeOffsetOfEntryHeader = (long)DataConverter.LittleEndian.GetUInt64(DataBytes, 0);
VolumeNumber = DataConverter.LittleEndian.GetUInt32(DataBytes, 8);
return;
case 16:
UncompressedSize = (long)DataConverter.LittleEndian.GetUInt64(DataBytes, 0);
CompressedSize = (long)DataConverter.LittleEndian.GetUInt64(DataBytes, 8);
return;
case 20:
UncompressedSize = (long)DataConverter.LittleEndian.GetUInt64(DataBytes, 0);
CompressedSize = (long)DataConverter.LittleEndian.GetUInt64(DataBytes, 8);
VolumeNumber = DataConverter.LittleEndian.GetUInt32(DataBytes, 16);
return;
case 24:
UncompressedSize = (long)DataConverter.LittleEndian.GetUInt64(DataBytes, 0);
CompressedSize = (long)DataConverter.LittleEndian.GetUInt64(DataBytes, 8);
RelativeOffsetOfEntryHeader = (long)DataConverter.LittleEndian.GetUInt64(DataBytes, 16);
return;
case 28:
UncompressedSize = (long)DataConverter.LittleEndian.GetUInt64(DataBytes, 0);
CompressedSize = (long)DataConverter.LittleEndian.GetUInt64(DataBytes, 8);
RelativeOffsetOfEntryHeader = (long)DataConverter.LittleEndian.GetUInt64(DataBytes, 16);
VolumeNumber = DataConverter.LittleEndian.GetUInt32(DataBytes, 24);
return;
default:
throw new ArchiveException("Unexpected size of of Zip64 extended information extra field");
}
}
public long UncompressedSize { get; private set; }
public long CompressedSize { get; private set; }
public long RelativeOffsetOfEntryHeader { get; private set; }
public uint VolumeNumber { get; private set; }
}
internal static class LocalEntryHeaderExtraFactory
{
internal static ExtraData Create(ExtraDataType type, ushort length, byte[] extraData)
@@ -60,6 +129,13 @@ namespace SharpCompress.Common.Zip.Headers
Length = length,
DataBytes = extraData
};
case ExtraDataType.Zip64ExtendedInformationExtraField:
return new Zip64ExtendedInformationExtraField
(
type,
length,
extraData
);
default:
return new ExtraData
{

View File

@@ -0,0 +1,54 @@
using System;
using System.IO;
namespace SharpCompress.Common.Zip.Headers
{
internal class Zip64DirectoryEndHeader : ZipHeader
{
public Zip64DirectoryEndHeader()
: base(ZipHeaderType.Zip64DirectoryEnd)
{
}
internal override void Read(BinaryReader reader)
{
SizeOfDirectoryEndRecord = (long)reader.ReadUInt64();
VersionMadeBy = reader.ReadUInt16();
VersionNeededToExtract = reader.ReadUInt16();
VolumeNumber = reader.ReadUInt32();
FirstVolumeWithDirectory = reader.ReadUInt32();
TotalNumberOfEntriesInDisk = (long)reader.ReadUInt64();
TotalNumberOfEntries = (long)reader.ReadUInt64();
DirectorySize = (long)reader.ReadUInt64();
DirectoryStartOffsetRelativeToDisk = (long)reader.ReadUInt64();
DataSector = reader.ReadBytes((int)(SizeOfDirectoryEndRecord - SizeOfFixedHeaderDataExceptSignatureAndSizeFields));
}
const int SizeOfFixedHeaderDataExceptSignatureAndSizeFields = 44;
internal override void Write(BinaryWriter writer)
{
throw new System.NotImplementedException();
}
public long SizeOfDirectoryEndRecord { get; private set; }
public ushort VersionMadeBy { get; private set; }
public ushort VersionNeededToExtract { get; private set; }
public uint VolumeNumber { get; private set; }
public uint FirstVolumeWithDirectory { get; private set; }
public long TotalNumberOfEntriesInDisk { get; private set; }
public long TotalNumberOfEntries { get; private set; }
public long DirectorySize { get; private set; }
public long DirectoryStartOffsetRelativeToDisk { get; private set; }
public byte[] DataSector { get; private set; }
}
}

View File

@@ -0,0 +1,30 @@
using System.IO;
namespace SharpCompress.Common.Zip.Headers
{
internal class Zip64DirectoryEndLocatorHeader : ZipHeader
{
public Zip64DirectoryEndLocatorHeader()
: base(ZipHeaderType.Zip64DirectoryEndLocator)
{
}
internal override void Read(BinaryReader reader)
{
FirstVolumeWithDirectory = reader.ReadUInt32();
RelativeOffsetOfTheEndOfDirectoryRecord = (long)reader.ReadUInt64();
TotalNumberOfVolumes = reader.ReadUInt32();
}
internal override void Write(BinaryWriter writer)
{
throw new System.NotImplementedException();
}
public uint FirstVolumeWithDirectory { get; private set; }
public long RelativeOffsetOfTheEndOfDirectoryRecord { get; private set; }
public uint TotalNumberOfVolumes { get; private set; }
}
}

View File

@@ -57,15 +57,31 @@ namespace SharpCompress.Common.Zip.Headers
internal ZipCompressionMethod CompressionMethod { get; set; }
internal uint CompressedSize { get; set; }
internal long CompressedSize { get; set; }
internal long? DataStartPosition { get; set; }
internal uint UncompressedSize { get; set; }
internal long UncompressedSize { get; set; }
internal List<ExtraData> Extra { get; set; }
public string Password { get; set; }
internal PkwareTraditionalEncryptionData ComposeEncryptionData(Stream archiveStream)
{
if (archiveStream == null)
{
throw new ArgumentNullException(nameof(archiveStream));
}
var buffer = new byte[12];
archiveStream.Read(buffer, 0, 12);
PkwareTraditionalEncryptionData encryptionData = PkwareTraditionalEncryptionData.ForRead(Password, this, buffer);
return encryptionData;
}
internal PkwareTraditionalEncryptionData PkwareTraditionalEncryptionData { get; set; }
#if !NO_CRYPTO
internal WinzipAesEncryptionData WinzipAesEncryptionData { get; set; }
#endif
@@ -96,5 +112,7 @@ namespace SharpCompress.Common.Zip.Headers
}
internal ZipFilePart Part { get; set; }
internal bool IsZip64 { get { return CompressedSize == uint.MaxValue; } }
}
}

View File

@@ -6,6 +6,8 @@
LocalEntry,
DirectoryEntry,
DirectoryEnd,
Split
Split,
Zip64DirectoryEnd,
Zip64DirectoryEndLocator
}
}

View File

@@ -9,6 +9,7 @@ namespace SharpCompress.Common.Zip
internal class SeekableZipHeaderFactory : ZipHeaderFactory
{
private const int MAX_ITERATIONS_FOR_DIRECTORY_HEADER = 4096;
private bool zip64;
internal SeekableZipHeaderFactory(string password)
: base(StreamingMode.Seekable, password)
@@ -16,11 +17,56 @@ namespace SharpCompress.Common.Zip
}
internal IEnumerable<DirectoryEntryHeader> ReadSeekableHeader(Stream stream)
{
var reader = new BinaryReader(stream);
SeekBackToHeader(stream, reader, DIRECTORY_END_HEADER_BYTES);
var entry = new DirectoryEndHeader();
entry.Read(reader);
if (entry.IsZip64)
{
zip64 = true;
SeekBackToHeader(stream, reader, ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR);
var zip64Locator = new Zip64DirectoryEndLocatorHeader();
zip64Locator.Read(reader);
stream.Seek(zip64Locator.RelativeOffsetOfTheEndOfDirectoryRecord, SeekOrigin.Begin);
uint zip64Signature = reader.ReadUInt32();
if(zip64Signature != ZIP64_END_OF_CENTRAL_DIRECTORY)
throw new ArchiveException("Failed to locate the Zip64 Header");
var zip64Entry = new Zip64DirectoryEndHeader();
zip64Entry.Read(reader);
stream.Seek(zip64Entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin);
}
else
{
stream.Seek(entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin);
}
long position = stream.Position;
while (true)
{
stream.Position = position;
uint signature = reader.ReadUInt32();
var directoryEntryHeader = ReadHeader(signature, reader, zip64) as DirectoryEntryHeader;
position = stream.Position;
if (directoryEntryHeader == null)
{
yield break;
}
//entry could be zero bytes so we need to know that.
directoryEntryHeader.HasData = directoryEntryHeader.CompressedSize != 0;
yield return directoryEntryHeader;
}
}
private static void SeekBackToHeader(Stream stream, BinaryReader reader, uint headerSignature)
{
long offset = 0;
uint signature;
BinaryReader reader = new BinaryReader(stream);
int iterationCount = 0;
do
{
@@ -34,33 +80,10 @@ namespace SharpCompress.Common.Zip
iterationCount++;
if (iterationCount > MAX_ITERATIONS_FOR_DIRECTORY_HEADER)
{
throw new ArchiveException(
"Could not find Zip file Directory at the end of the file. File may be corrupted.");
throw new ArchiveException("Could not find Zip file Directory at the end of the file. File may be corrupted.");
}
}
while (signature != DIRECTORY_END_HEADER_BYTES);
var entry = new DirectoryEndHeader();
entry.Read(reader);
stream.Seek(entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin);
DirectoryEntryHeader directoryEntryHeader = null;
long position = stream.Position;
while (true)
{
stream.Position = position;
signature = reader.ReadUInt32();
directoryEntryHeader = ReadHeader(signature, reader) as DirectoryEntryHeader;
position = stream.Position;
if (directoryEntryHeader == null)
{
yield break;
}
//entry could be zero bytes so we need to know that.
directoryEntryHeader.HasData = directoryEntryHeader.CompressedSize != 0;
yield return directoryEntryHeader;
}
while (signature != headerSignature);
}
internal LocalEntryHeader GetLocalHeader(Stream stream, DirectoryEntryHeader directoryEntryHeader)
@@ -68,7 +91,7 @@ namespace SharpCompress.Common.Zip
stream.Seek(directoryEntryHeader.RelativeOffsetOfEntryHeader, SeekOrigin.Begin);
BinaryReader reader = new BinaryReader(stream);
uint signature = reader.ReadUInt32();
var localEntryHeader = ReadHeader(signature, reader) as LocalEntryHeader;
var localEntryHeader = ReadHeader(signature, reader, zip64) as LocalEntryHeader;
if (localEntryHeader == null)
{
throw new InvalidOperationException();

View File

@@ -28,7 +28,7 @@ namespace SharpCompress.Common.Zip
ZipHeader header = null;
BinaryReader reader = new BinaryReader(rewindableStream);
if (lastEntryHeader != null &&
FlagUtility.HasFlag(lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor))
(FlagUtility.HasFlag(lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor) || lastEntryHeader.IsZip64))
{
reader = (lastEntryHeader.Part as StreamingZipFilePart).FixStreamedFileLocation(ref rewindableStream);
long? pos = rewindableStream.CanSeek ? (long?)rewindableStream.Position : null;

View File

@@ -51,7 +51,7 @@ namespace SharpCompress.Common.Zip
protected abstract Stream CreateBaseStream();
protected bool LeaveStreamOpen { get { return FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor); } }
protected bool LeaveStreamOpen { get { return FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor) || Header.IsZip64; } }
protected Stream CreateDecompressionStream(Stream stream)
{
@@ -126,18 +126,16 @@ namespace SharpCompress.Common.Zip
protected Stream GetCryptoStream(Stream plainStream)
{
if ((Header.CompressedSize == 0)
#if !NO_CRYPTO
&& ((Header.PkwareTraditionalEncryptionData != null)
|| (Header.WinzipAesEncryptionData != null)))
#else
&& (Header.PkwareTraditionalEncryptionData != null))
#endif
bool isFileEncrypted = FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted);
if (Header.CompressedSize == 0 && isFileEncrypted)
{
throw new NotSupportedException("Cannot encrypt file with unknown size at start.");
}
if ((Header.CompressedSize == 0)
if ((Header.CompressedSize == 0
&& FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor))
|| Header.IsZip64)
{
plainStream = new NonDisposingStream(plainStream); //make sure AES doesn't close
}
@@ -145,18 +143,40 @@ namespace SharpCompress.Common.Zip
{
plainStream = new ReadOnlySubStream(plainStream, Header.CompressedSize); //make sure AES doesn't close
}
if (Header.PkwareTraditionalEncryptionData != null)
if (isFileEncrypted)
{
return new PkwareTraditionalCryptoStream(plainStream, Header.PkwareTraditionalEncryptionData,
CryptoMode.Decrypt);
}
switch (Header.CompressionMethod)
{
case ZipCompressionMethod.None:
case ZipCompressionMethod.Deflate:
case ZipCompressionMethod.Deflate64:
case ZipCompressionMethod.BZip2:
case ZipCompressionMethod.LZMA:
case ZipCompressionMethod.PPMd:
{
return new PkwareTraditionalCryptoStream(plainStream, Header.ComposeEncryptionData(plainStream), CryptoMode.Decrypt);
}
case ZipCompressionMethod.WinzipAes:
{
#if !NO_FILE
if (Header.WinzipAesEncryptionData != null)
{
//only read 10 less because the last ten are auth bytes
return new WinzipAesCryptoStream(plainStream, Header.WinzipAesEncryptionData, Header.CompressedSize - 10);
}
if (Header.WinzipAesEncryptionData != null)
{
return new WinzipAesCryptoStream(plainStream, Header.WinzipAesEncryptionData, Header.CompressedSize - 10);
}
#endif
return plainStream;
}
default:
{
throw new ArgumentOutOfRangeException();
}
}
}
return plainStream;
}
}

View File

@@ -17,8 +17,8 @@ namespace SharpCompress.Common.Zip
internal const uint DIGITAL_SIGNATURE = 0x05054b50;
internal const uint SPLIT_ARCHIVE_HEADER_BYTES = 0x30304b50;
private const uint ZIP64_END_OF_CENTRAL_DIRECTORY = 0x06064b50;
private const uint ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR = 0x07064b50;
internal const uint ZIP64_END_OF_CENTRAL_DIRECTORY = 0x06064b50;
internal const uint ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR = 0x07064b50;
protected LocalEntryHeader lastEntryHeader;
private readonly string password;
@@ -30,7 +30,7 @@ namespace SharpCompress.Common.Zip
this.password = password;
}
protected ZipHeader ReadHeader(uint headerBytes, BinaryReader reader)
protected ZipHeader ReadHeader(uint headerBytes, BinaryReader reader, bool zip64 = false)
{
switch (headerBytes)
{
@@ -54,14 +54,12 @@ namespace SharpCompress.Common.Zip
if (FlagUtility.HasFlag(lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor))
{
lastEntryHeader.Crc = reader.ReadUInt32();
lastEntryHeader.CompressedSize = reader.ReadUInt32();
lastEntryHeader.UncompressedSize = reader.ReadUInt32();
lastEntryHeader.CompressedSize = zip64 ? (long)reader.ReadUInt64() : reader.ReadUInt32();
lastEntryHeader.UncompressedSize = zip64 ? (long)reader.ReadUInt64() : reader.ReadUInt32();
}
else
{
reader.ReadUInt32();
reader.ReadUInt32();
reader.ReadUInt32();
reader.ReadBytes(zip64 ? 20 : 12);
}
return null;
}
@@ -78,9 +76,14 @@ namespace SharpCompress.Common.Zip
return new SplitHeader();
}
case ZIP64_END_OF_CENTRAL_DIRECTORY:
{
var entry = new Zip64DirectoryEndHeader();
entry.Read(reader);
return entry;
}
case ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR:
{
var entry = new IgnoreHeader(ZipHeaderType.Ignore);
var entry = new Zip64DirectoryEndLocatorHeader();
entry.Read(reader);
return entry;
}
@@ -111,46 +114,43 @@ namespace SharpCompress.Common.Zip
{
if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.Encrypted))
{
if (!entryHeader.IsDirectory &&
entryHeader.CompressedSize == 0 &&
if (!entryHeader.IsDirectory && entryHeader.CompressedSize == 0 &&
FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor))
{
throw new NotSupportedException(
"SharpCompress cannot currently read non-seekable Zip Streams with encrypted data that has been written in a non-seekable manner.");
throw new NotSupportedException("SharpCompress cannot currently read non-seekable Zip Streams with encrypted data that has been written in a non-seekable manner.");
}
if (password == null)
{
throw new CryptographicException("No password supplied for encrypted zip.");
}
if (entryHeader.CompressionMethod != ZipCompressionMethod.WinzipAes)
{
byte[] buffer = new byte[12];
stream.Read(buffer, 0, 12);
entryHeader.PkwareTraditionalEncryptionData = PkwareTraditionalEncryptionData.ForRead(password,
entryHeader,
buffer);
entryHeader.CompressedSize -= 12;
}
else
entryHeader.Password = password;
if (entryHeader.CompressionMethod == ZipCompressionMethod.WinzipAes)
{
#if NO_CRYPTO
throw new NotSupportedException("Cannot decrypt Winzip AES with Silverlight or WP7.");
#else
var data = entryHeader.Extra.SingleOrDefault(x => x.Type == ExtraDataType.WinZipAes);
WinzipAesKeySize keySize = (WinzipAesKeySize) data.DataBytes[4];
ExtraData data = entryHeader.Extra.SingleOrDefault(x => x.Type == ExtraDataType.WinZipAes);
if (data != null)
{
var keySize = (WinzipAesKeySize)data.DataBytes[4];
byte[] salt = new byte[WinzipAesEncryptionData.KeyLengthInBytes(keySize)/2];
byte[] passwordVerifyValue = new byte[2];
stream.Read(salt, 0, salt.Length);
stream.Read(passwordVerifyValue, 0, 2);
entryHeader.WinzipAesEncryptionData = new WinzipAesEncryptionData(keySize, salt, passwordVerifyValue,
password);
entryHeader.CompressedSize -= (uint) (salt.Length + 2);
var salt = new byte[WinzipAesEncryptionData.KeyLengthInBytes(keySize) / 2];
var passwordVerifyValue = new byte[2];
stream.Read(salt, 0, salt.Length);
stream.Read(passwordVerifyValue, 0, 2);
entryHeader.WinzipAesEncryptionData =
new WinzipAesEncryptionData(keySize, salt, passwordVerifyValue, password);
entryHeader.CompressedSize -= (uint)(salt.Length + 2);
}
#endif
}
}
if (entryHeader.IsDirectory)
{
return;
@@ -168,13 +168,15 @@ namespace SharpCompress.Common.Zip
{
entryHeader.DataStartPosition = stream.Position;
stream.Position += entryHeader.CompressedSize;
}
break;
}
case StreamingMode.Streaming:
{
entryHeader.PackedStream = stream;
}
break;
}
default:
{
throw new InvalidFormatException("Invalid StreamingMode");

View File

@@ -1,5 +1,5 @@
{
"version": "0.14.0",
"version": "0.15.2",
"title": "SharpCompress - Pure C# Decompression/Compression",
"authors": [ "Adam Hathcock" ],
"language": "en-US",
@@ -13,7 +13,8 @@
},
"buildOptions": {
"warningsAsErrors": true,
"allowUnsafe": true
"allowUnsafe": true,
"keyFile": "../../SharpCompress.snk"
},
"frameworks": {
"net35": {

View File

@@ -46,7 +46,7 @@ namespace SharpCompress.Test
[Fact]
public void GZip_Archive_NoAdd()
{
string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg\\test.jpg");
string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg");
ResetScratch();
using (Stream stream = File.Open(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"), FileMode.Open))
using (var archive = GZipArchive.Open(stream))
@@ -55,5 +55,47 @@ namespace SharpCompress.Test
archive.SaveTo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"));
}
}
[Fact]
public void GZip_Archive_Multiple_Reads()
{
ResetScratch();
var inputStream = new MemoryStream();
using (var fileStream = File.Open(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"), FileMode.Open))
{
fileStream.CopyTo(inputStream);
inputStream.Position = 0;
}
using (var archive = GZipArchive.Open(inputStream))
{
var archiveEntry = archive.Entries.First();
MemoryStream tarStream;
using (var entryStream = archiveEntry.OpenEntryStream())
{
tarStream = new MemoryStream();
entryStream.CopyTo(tarStream);
}
var size = tarStream.Length;
using (var entryStream = archiveEntry.OpenEntryStream())
{
tarStream = new MemoryStream();
entryStream.CopyTo(tarStream);
}
Assert.Equal(size, tarStream.Length);
using (var entryStream = archiveEntry.OpenEntryStream())
{
var result = SharpCompress.Archives.Tar.TarArchive.IsTarFile(entryStream);
}
Assert.Equal(size, tarStream.Length);
using (var entryStream = archiveEntry.OpenEntryStream())
{
tarStream = new MemoryStream();
entryStream.CopyTo(tarStream);
}
Assert.Equal(size, tarStream.Length);
}
}
}
}

View File

@@ -115,7 +115,7 @@ namespace SharpCompress.Test
[Fact]
public void Tar_Random_Write_Add()
{
string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg\\test.jpg");
string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg","test.jpg");
string scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Tar.mod.tar");
string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.mod.tar");
string modified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar");

View File

@@ -210,16 +210,26 @@ namespace SharpCompress.Test
protected void CompareArchivesByPath(string file1, string file2)
{
//don't compare the order. OS X reads files from the file system in a different order therefore makes the archive ordering different
var archive1Entries = new List<string>();
var archive2Entries = new List<string>();
using (var archive1 = ReaderFactory.Open(File.OpenRead(file1)))
using (var archive2 = ReaderFactory.Open(File.OpenRead(file2)))
{
while (archive1.MoveToNextEntry())
{
Assert.True(archive2.MoveToNextEntry());
Assert.Equal(archive1.Entry.Key, archive2.Entry.Key);
archive1Entries.Add(archive1.Entry.Key);
archive2Entries.Add(archive2.Entry.Key);
}
Assert.False(archive2.MoveToNextEntry());
}
archive1Entries.Sort();
archive2Entries.Sort();
for (int i = 0; i < archive1Entries.Count; i++)
{
Assert.Equal(archive1Entries[i], archive2Entries[i]);
}
}
private static object lockObject = new object();

View File

@@ -25,7 +25,6 @@ namespace SharpCompress.Test
ArchiveStreamRead("Zip.zipx");
}
[Fact]
public void Zip_BZip2_Streamed_ArchiveStreamRead()
{
@@ -130,6 +129,18 @@ namespace SharpCompress.Test
ArchiveFileRead("Zip.none.zip");
}
[Fact]
public void Zip_Zip64_ArchiveStreamRead()
{
ArchiveStreamRead("Zip.zip64.zip");
}
[Fact]
public void Zip_Zip64_ArchiveFileRead()
{
ArchiveFileRead("Zip.zip64.zip");
}
[Fact]
public void Zip_Random_Write_Remove()
{
@@ -150,7 +161,7 @@ namespace SharpCompress.Test
[Fact]
public void Zip_Random_Write_Add()
{
string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg\\test.jpg");
string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg","test.jpg");
string scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Zip.deflate.mod.zip");
string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.mod.zip");
string modified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.mod2.zip");
@@ -374,6 +385,29 @@ namespace SharpCompress.Test
Assert.Equal(count3, 3);
}
[Fact]
public void Zip_Deflate_PKWear_Multipy_Entry_Access()
{
string zipFile = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.pkware.zip");
using (FileStream fileStream = File.Open(zipFile, FileMode.Open))
{
using (IArchive archive = ArchiveFactory.Open(fileStream, new ReaderOptions { Password = "12345678" }))
{
var entries = archive.Entries.Where(entry => !entry.IsDirectory);
foreach (IArchiveEntry entry in entries)
{
for (var i = 0; i < 100; i++)
{
using (var memoryStream = new MemoryStream())
using (Stream entryStream = entry.OpenEntryStream())
entryStream.CopyTo(memoryStream);
}
}
}
}
}
class NonSeekableMemoryStream : MemoryStream
{

View File

@@ -14,6 +14,13 @@ namespace SharpCompress.Test
{
UseExtensionInsteadOfNameToVerify = true;
}
[Fact]
public void Zip_Zip64_Streamed_Read()
{
Read("Zip.Zip64.zip", CompressionType.Deflate);
}
[Fact]
public void Zip_ZipX_Streamed_Read()
{

View File

@@ -1,4 +1,8 @@
{
"buildOptions": {
"keyFile": "../../SharpCompress.snk"
},
"testRunner": "xunit",
"frameworks": {

Binary file not shown.

Binary file not shown.