Compare commits

...

3 Commits

Author SHA1 Message Date
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
14 changed files with 186 additions and 48 deletions

View File

@@ -15,7 +15,7 @@ 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 with the Archive API
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

View File

@@ -25,12 +25,16 @@ 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.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)
@@ -123,8 +127,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,4 +1,4 @@
version: '0.13.{build}'
version: '0.15.{build}'
init:
- git config --global core.autocrlf true

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

@@ -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,11 +57,11 @@ 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; }
@@ -112,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

@@ -133,8 +133,9 @@ namespace SharpCompress.Common.Zip
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
}

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;
}

View File

@@ -1,5 +1,5 @@
{
"version": "0.14.1",
"version": "0.15.0",
"title": "SharpCompress - Pure C# Decompression/Compression",
"authors": [ "Adam Hathcock" ],
"language": "en-US",

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()
{

Binary file not shown.