Skip to content

Commit

Permalink
Merge pull request #206 from markryd/zip64-extraction
Browse files Browse the repository at this point in the history
Zip64 extending information and ZipReader
  • Loading branch information
adamhathcock authored Jan 25, 2017
2 parents 44d54db + a8c3a74 commit 7c6f050
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 5 deletions.
22 changes: 20 additions & 2 deletions src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Linq;

namespace SharpCompress.Common.Zip.Headers
Expand Down Expand Up @@ -41,6 +42,23 @@ internal override void Read(BinaryReader reader)
{
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)
Expand Down Expand Up @@ -77,7 +95,7 @@ internal override void Write(BinaryWriter writer)

public ushort VersionNeededToExtract { get; set; }

public uint RelativeOffsetOfEntryHeader { get; set; }
public long RelativeOffsetOfEntryHeader { get; set; }

public uint ExternalFileAttributes { get; set; }

Expand Down
13 changes: 13 additions & 0 deletions src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ internal override void Read(BinaryReader reader)
{
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)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Text;
using SharpCompress.Converters;

namespace SharpCompress.Common.Zip.Headers
{
Expand All @@ -11,7 +12,8 @@ internal enum ExtraDataType : ushort

// Third Party Mappings
// -Info-ZIP Unicode Path Extra Field
UnicodePathExtraField = 0x7075
UnicodePathExtraField = 0x7075,
Zip64ExtendedInformationExtraField = 0x0001
}

internal class ExtraData
Expand Down Expand Up @@ -47,6 +49,73 @@ internal string UnicodeName
}
}

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)
Expand All @@ -60,6 +129,13 @@ internal static ExtraData Create(ExtraDataType type, ushort length, byte[] extra
Length = length,
DataBytes = extraData
};
case ExtraDataType.Zip64ExtendedInformationExtraField:
return new Zip64ExtendedInformationExtraField
(
type,
length,
extraData
);
default:
return new ExtraData
{
Expand Down
2 changes: 1 addition & 1 deletion src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ internal IEnumerable<ZipHeader> ReadStreamHeader(Stream stream)
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;
Expand Down
2 changes: 1 addition & 1 deletion src/SharpCompress/Common/Zip/ZipFilePart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ internal override Stream GetRawStream()

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)
{
Expand Down
7 changes: 7 additions & 0 deletions test/SharpCompress.Test/Zip/ZipReaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ public ZipReaderTests()
{
UseExtensionInsteadOfNameToVerify = true;
}

[Fact]
public void Zip_Zip64_Streamed_Read()
{
Read("Zip.Zip64.zip", CompressionType.Deflate);
}

[Fact]
public void Zip_ZipX_Streamed_Read()
{
Expand Down

0 comments on commit 7c6f050

Please sign in to comment.