mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-11-15 17:02:03 +00:00
Merge pull request #1473 from adamhathcock/review/fix-zip64-descriptor-width
Fix ZIP64 streaming data descriptor width parsing
This commit is contained in:
@@ -56,6 +56,15 @@ internal partial class LocalEntryHeader : ZipFileEntry
|
||||
}
|
||||
|
||||
var zip64ExtraData = Extra.OfType<Zip64ExtendedInformationExtraField>().FirstOrDefault();
|
||||
|
||||
// Remember ZIP64 before the extra field replaces size sentinels with values
|
||||
// that may be small (or zero placeholders). Descriptor width must not depend
|
||||
// on those values, nor on Version, which other compression methods also raise.
|
||||
UsesZip64Sizes =
|
||||
CompressedSize == uint.MaxValue
|
||||
|| UncompressedSize == uint.MaxValue
|
||||
|| zip64ExtraData is not null;
|
||||
|
||||
if (zip64ExtraData != null)
|
||||
{
|
||||
zip64ExtraData.Process(UncompressedSize, CompressedSize, 0, 0);
|
||||
@@ -102,4 +111,10 @@ internal partial class LocalEntryHeader : ZipFileEntry
|
||||
}
|
||||
|
||||
internal ushort Version { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the local header declares ZIP64 sizes, requiring 8-byte descriptor sizes
|
||||
/// even for small entries (APPNOTE 4.3.9.2).
|
||||
/// </summary>
|
||||
internal bool UsesZip64Sizes { get; private set; }
|
||||
}
|
||||
|
||||
@@ -128,44 +128,57 @@ internal sealed partial class StreamingZipHeaderFactory
|
||||
lastEntryHeader.Crc = crc;
|
||||
lastEntryHeader.IsCrcAvailable = true;
|
||||
|
||||
//attempt 32bit read
|
||||
ulong compressedSize = await _reader
|
||||
.ReadUInt32Async(_cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
ulong uncompressedSize = await _reader
|
||||
.ReadUInt32Async(_cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
headerBytes = await _reader
|
||||
.ReadUInt32Async(_cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
ulong compressedSize;
|
||||
ulong uncompressedSize;
|
||||
|
||||
//check for zip64 sentinel or unexpected header
|
||||
bool isSentinel =
|
||||
compressedSize == 0xFFFFFFFF || uncompressedSize == 0xFFFFFFFF;
|
||||
bool isHeader = headerBytes == 0x04034b50 || headerBytes == 0x02014b50;
|
||||
|
||||
if (!isHeader && !isSentinel)
|
||||
// A ZIP64 size's low word can equal a header signature. Honor the
|
||||
// declared width rather than mistaking that word for the next header,
|
||||
// even if the record following the descriptor is unrecognized.
|
||||
if (lastEntryHeader.UsesZip64Sizes)
|
||||
{
|
||||
//reshuffle into 64-bit values
|
||||
compressedSize = (uncompressedSize << 32) | compressedSize;
|
||||
// headerBytes holds the low word; the high word follows on the wire.
|
||||
var highUncompressedSize = await _reader
|
||||
.ReadUInt32Async(_cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
uncompressedSize = headerBytes | ((ulong)highUncompressedSize << 32);
|
||||
headerBytes = await _reader
|
||||
.ReadUInt32Async(_cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else if (isSentinel)
|
||||
{
|
||||
//standards-compliant zip64 descriptor
|
||||
compressedSize = await _reader
|
||||
.ReadUInt64Async(_cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
uncompressedSize = await _reader
|
||||
.ReadUInt64Async(_cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
headerBytes = await _reader
|
||||
.ReadUInt32Async(_cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Without ZIP64 metadata, retain compatibility with older writers
|
||||
// that emitted 64-bit descriptors without declaring them locally.
|
||||
compressedSize = await _reader
|
||||
.ReadUInt32Async(_cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
uncompressedSize = await _reader
|
||||
.ReadUInt32Async(_cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
headerBytes = await _reader
|
||||
.ReadUInt32Async(_cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// Keep the legacy probe limited to local/central headers: other
|
||||
// record signatures can be the low word of a 64-bit size.
|
||||
if (
|
||||
headerBytes != ENTRY_HEADER_BYTES
|
||||
&& headerBytes != DIRECTORY_START_HEADER_BYTES
|
||||
)
|
||||
{
|
||||
// Descriptor words are size data, including 0xFFFFFFFF; unlike
|
||||
// local headers, descriptors do not contain size sentinels.
|
||||
compressedSize = (uncompressedSize << 32) | compressedSize;
|
||||
// headerBytes holds the low word; the high word follows on the wire.
|
||||
var highUncompressedSize = await _reader
|
||||
.ReadUInt32Async(_cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
uncompressedSize = headerBytes | ((ulong)highUncompressedSize << 32);
|
||||
headerBytes = await _reader
|
||||
.ReadUInt32Async(_cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
lastEntryHeader.CompressedSize = (long)compressedSize;
|
||||
|
||||
@@ -68,28 +68,40 @@ internal sealed partial class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
_lastEntryHeader.Crc = crc;
|
||||
_lastEntryHeader.IsCrcAvailable = true;
|
||||
|
||||
//attempt 32bit read
|
||||
ulong compSize = reader.ReadUInt32();
|
||||
ulong uncompSize = reader.ReadUInt32();
|
||||
headerBytes = reader.ReadUInt32();
|
||||
ulong compSize;
|
||||
ulong uncompSize;
|
||||
|
||||
//check for zip64 sentinel or unexpected header
|
||||
bool isSentinel = compSize == 0xFFFFFFFF || uncompSize == 0xFFFFFFFF;
|
||||
bool isHeader = headerBytes == 0x04034b50 || headerBytes == 0x02014b50;
|
||||
|
||||
if (!isHeader && !isSentinel)
|
||||
// A ZIP64 size's low word can equal a header signature. Honor the
|
||||
// declared width rather than mistaking that word for the next header,
|
||||
// even if the record following the descriptor is unrecognized.
|
||||
if (_lastEntryHeader.UsesZip64Sizes)
|
||||
{
|
||||
//reshuffle into 64-bit values
|
||||
compSize = (uncompSize << 32) | compSize;
|
||||
// headerBytes holds the low word; the high word follows on the wire.
|
||||
uncompSize = headerBytes | ((ulong)reader.ReadUInt32() << 32);
|
||||
headerBytes = reader.ReadUInt32();
|
||||
}
|
||||
else if (isSentinel)
|
||||
{
|
||||
//standards-compliant zip64 descriptor
|
||||
compSize = reader.ReadUInt64();
|
||||
uncompSize = reader.ReadUInt64();
|
||||
headerBytes = reader.ReadUInt32();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Without ZIP64 metadata, retain compatibility with older writers
|
||||
// that emitted 64-bit descriptors without declaring them locally.
|
||||
compSize = reader.ReadUInt32();
|
||||
uncompSize = reader.ReadUInt32();
|
||||
headerBytes = reader.ReadUInt32();
|
||||
|
||||
// Keep the legacy probe limited to local/central headers: other
|
||||
// record signatures can be the low word of a 64-bit size.
|
||||
if (
|
||||
headerBytes != ENTRY_HEADER_BYTES
|
||||
&& headerBytes != DIRECTORY_START_HEADER_BYTES
|
||||
)
|
||||
{
|
||||
// Descriptor words are size data, including 0xFFFFFFFF; unlike
|
||||
// local headers, descriptors do not contain size sentinels.
|
||||
compSize = (uncompSize << 32) | compSize;
|
||||
// headerBytes holds the low word; the high word follows on the wire.
|
||||
uncompSize = headerBytes | ((ulong)reader.ReadUInt32() << 32);
|
||||
headerBytes = reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
_lastEntryHeader.CompressedSize = (long)compSize;
|
||||
|
||||
Reference in New Issue
Block a user