diff --git a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs index f776f0e16..25fb06be2 100644 --- a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Linq; namespace SharpCompress.Common.Zip.Headers @@ -41,6 +42,23 @@ internal override void Read(BinaryReader reader) { Name = ((ExtraUnicodePathExtraField)unicodePathExtra).UnicodeName; } + + var zip64ExtraData = Extra.OfType().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) @@ -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; } diff --git a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs index 666d683fc..1d0720710 100644 --- a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs @@ -32,6 +32,19 @@ internal override void Read(BinaryReader reader) { Name = ((ExtraUnicodePathExtraField)unicodePathExtra).UnicodeName; } + + var zip64ExtraData = Extra.OfType().FirstOrDefault(); + if (zip64ExtraData != null) + { + if (CompressedSize == uint.MaxValue) + { + CompressedSize = zip64ExtraData.CompressedSize; + } + if (UncompressedSize == uint.MaxValue) + { + UncompressedSize = zip64ExtraData.UncompressedSize; + } + } } internal override void Write(BinaryWriter writer) diff --git a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs index 3cc798508..14033ab61 100644 --- a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs +++ b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs @@ -1,5 +1,6 @@ using System; using System.Text; +using SharpCompress.Converters; namespace SharpCompress.Common.Zip.Headers { @@ -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 @@ -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) @@ -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 { diff --git a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs index 7d15ea218..9c8015edd 100644 --- a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs @@ -28,7 +28,7 @@ internal IEnumerable 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; diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs index 712b606c1..5c9a68b1e 100644 --- a/src/SharpCompress/Common/Zip/ZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs @@ -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) { diff --git a/test/SharpCompress.Test/Zip/ZipReaderTests.cs b/test/SharpCompress.Test/Zip/ZipReaderTests.cs index 115138eb0..35db12789 100644 --- a/test/SharpCompress.Test/Zip/ZipReaderTests.cs +++ b/test/SharpCompress.Test/Zip/ZipReaderTests.cs @@ -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() {