mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-04 05:25:00 +00:00
Merge pull request #205 from markryd/zip64-extraction
Add zip64 support for ZipArchive extraction
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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; } }
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@
|
||||
LocalEntry,
|
||||
DirectoryEntry,
|
||||
DirectoryEnd,
|
||||
Split
|
||||
Split,
|
||||
Zip64DirectoryEnd,
|
||||
Zip64DirectoryEndLocator
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
BIN
test/TestArchives/Archives/Zip.zip64.zip
Normal file
BIN
test/TestArchives/Archives/Zip.zip64.zip
Normal file
Binary file not shown.
Reference in New Issue
Block a user