Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -530,14 +530,11 @@ private void ReadCentralDirectory()
while (continueReadingCentralDirectory
&& currPosition + ZipCentralDirectoryFileHeader.BlockConstantSectionSize < sizedFileBuffer.Length)
{
ZipCentralDirectoryFileHeader currentHeader = new();

continueReadingCentralDirectory = continueReadingCentralDirectory &&
ZipCentralDirectoryFileHeader.TryReadBlock(sizedFileBuffer.Slice(currPosition), _archiveStream,
saveExtraFieldsAndComments, out bytesConsumed, out currentHeader);

if (!continueReadingCentralDirectory)
if (!continueReadingCentralDirectory ||
Comment thread
carlossanlop marked this conversation as resolved.
Outdated
!ZipCentralDirectoryFileHeader.TryReadBlock(sizedFileBuffer.Slice(currPosition), _archiveStream,
saveExtraFieldsAndComments, out bytesConsumed, out ZipCentralDirectoryFileHeader? currentHeader))
{
continueReadingCentralDirectory = false;
break;
}

Expand Down Expand Up @@ -662,8 +659,7 @@ private void TryReadZip64EndOfCentralDirectory(ZipEndOfCentralDirectoryBlock eoc
Zip64EndOfCentralDirectoryLocator.FieldLengths.Signature))
{
// use locator to get to Zip64-EOCD
Zip64EndOfCentralDirectoryLocator locator;
bool zip64eocdLocatorProper = Zip64EndOfCentralDirectoryLocator.TryReadBlock(_archiveStream, out locator);
bool zip64eocdLocatorProper = Zip64EndOfCentralDirectoryLocator.TryReadBlock(_archiveStream, out Zip64EndOfCentralDirectoryLocator locator);
Debug.Assert(zip64eocdLocatorProper); // we just found this using the signature finder, so it should be okay

if (locator.OffsetOfZip64EOCD > long.MaxValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -491,24 +491,24 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite)
Debug.Assert(_fileComment.Length <= ushort.MaxValue);

// decide if we need the Zip64 extra field:
Zip64ExtraField zip64ExtraField = new();
Zip64ExtraField? zip64ExtraField = null;
uint compressedSizeTruncated, uncompressedSizeTruncated, offsetOfLocalHeaderTruncated;

bool zip64Needed = false;

if (AreSizesTooLarge
#if DEBUG_FORCE_ZIP64
|| _archive._forceZip64
#endif
)
{
zip64Needed = true;
compressedSizeTruncated = ZipHelper.Mask32Bit;
uncompressedSizeTruncated = ZipHelper.Mask32Bit;

// If we have one of the sizes, the other must go in there as speced for LH, but not necessarily for CH, but we do it anyways
zip64ExtraField.CompressedSize = _compressedSize;
zip64ExtraField.UncompressedSize = _uncompressedSize;
zip64ExtraField = new()
{
CompressedSize = _compressedSize,
UncompressedSize = _uncompressedSize
};
}
else
{
Expand All @@ -523,27 +523,32 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite)
#endif
)
{
zip64Needed = true;
offsetOfLocalHeaderTruncated = ZipHelper.Mask32Bit;

// If we have one of the sizes, the other must go in there as speced for LH, but not necessarily for CH, but we do it anyways
zip64ExtraField.LocalHeaderOffset = _offsetOfLocalHeader;
zip64ExtraField = new()
{
LocalHeaderOffset = _offsetOfLocalHeader
};
}
else
{
offsetOfLocalHeaderTruncated = (uint)_offsetOfLocalHeader;
}

if (zip64Needed)
if (zip64ExtraField != null)
{
VersionToExtractAtLeast(ZipVersionNeededValues.Zip64);
}


// determine if we can fit zip64 extra field and original extra fields all in
int bigExtraFieldLength = (zip64Needed ? zip64ExtraField.TotalSize : 0)
int bigExtraFieldLength = (zip64ExtraField != null ? zip64ExtraField.TotalSize : 0)
+ (_cdUnknownExtraFields != null ? ZipGenericExtraField.TotalSize(_cdUnknownExtraFields) : 0);
ushort extraFieldLength;
if (bigExtraFieldLength > ushort.MaxValue)
{
extraFieldLength = (ushort)(zip64Needed ? zip64ExtraField.TotalSize : 0);
extraFieldLength = (ushort)(zip64ExtraField != null ? zip64ExtraField.TotalSize : 0);
_cdUnknownExtraFields = null;
}
else
Expand All @@ -555,7 +560,7 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite)
{
long centralDirectoryHeaderLength = ZipCentralDirectoryFileHeader.FieldLocations.DynamicData
+ _storedEntryNameBytes.Length
+ (zip64Needed ? zip64ExtraField.TotalSize : 0)
+ (zip64ExtraField != null ? zip64ExtraField.TotalSize : 0)
+ (_cdUnknownExtraFields != null ? ZipGenericExtraField.TotalSize(_cdUnknownExtraFields) : 0)
+ _fileComment.Length;

Expand Down Expand Up @@ -604,14 +609,18 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite)
_archive.ArchiveStream.Write(cdStaticHeader);
_archive.ArchiveStream.Write(_storedEntryNameBytes);

// write extra fields
if (zip64Needed)
zip64ExtraField.WriteBlock(_archive.ArchiveStream);
// write extra fields, and only write zip64ExtraField if we decided we need it (it's not null)
zip64ExtraField?.WriteBlock(_archive.ArchiveStream);

if (_cdUnknownExtraFields != null)
{
ZipGenericExtraField.WriteAllBlocks(_cdUnknownExtraFields, _archive.ArchiveStream);
}

if (_fileComment.Length > 0)
{
_archive.ArchiveStream.Write(_fileComment);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private static class FieldLengths
}
}

internal sealed partial class ZipLocalFileHeader
internal readonly partial struct ZipLocalFileHeader
{
internal static class FieldLengths
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ private static class FieldLocations
}
}

internal sealed partial class ZipLocalFileHeader
internal readonly partial struct ZipLocalFileHeader
{
internal static class FieldLocations
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

namespace System.IO.Compression
{
// All blocks.TryReadBlock do a check to see if signature is correct. Generic extra field is slightly different
// all of the TryReadBlocks will throw if there are not enough bytes in the stream

internal sealed partial class ZipGenericExtraField
Comment thread
carlossanlop marked this conversation as resolved.
{
private const int SizeOfHeader = FieldLengths.Tag + FieldLengths.Size;
Expand Down Expand Up @@ -506,7 +506,7 @@ public static void WriteBlock(Stream stream, long numberOfEntries, long startOfC
}
}

internal sealed partial class ZipLocalFileHeader
internal readonly partial struct ZipLocalFileHeader
{
// The Zip File Format Specification references 0x08074B50 and 0x04034B50, these are big endian representations.
// ZIP files store values in little endian, so these are reversed.
Expand Down Expand Up @@ -626,11 +626,12 @@ internal sealed partial class ZipCentralDirectoryFileHeader

// if saveExtraFieldsAndComments is false, FileComment and ExtraFields will be null
// in either case, the zip64 extra field info will be incorporated into other fields
public static bool TryReadBlock(ReadOnlySpan<byte> buffer, Stream furtherReads, bool saveExtraFieldsAndComments, out int bytesRead, out ZipCentralDirectoryFileHeader header)
public static bool TryReadBlock(ReadOnlySpan<byte> buffer, Stream furtherReads, bool saveExtraFieldsAndComments, out int bytesRead, [NotNullWhen(returnValue: true)] out ZipCentralDirectoryFileHeader? header)
{
header = null;

const int StackAllocationThreshold = 512;

header = new();
bytesRead = 0;

// the buffer will always be large enough for at least the constant section to be verified
Expand All @@ -641,26 +642,25 @@ public static bool TryReadBlock(ReadOnlySpan<byte> buffer, Stream furtherReads,
return false;
}

header.VersionMadeBySpecification = buffer[FieldLocations.VersionMadeBySpecification];
header.VersionMadeByCompatibility = buffer[FieldLocations.VersionMadeByCompatibility];
header.VersionNeededToExtract = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.VersionNeededToExtract..]);
header.GeneralPurposeBitFlag = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.GeneralPurposeBitFlags..]);
header.CompressionMethod = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.CompressionMethod..]);
header.LastModified = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.LastModified..]);
header.Crc32 = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.Crc32..]);
header = new()
{
VersionMadeBySpecification = buffer[FieldLocations.VersionMadeBySpecification],
VersionMadeByCompatibility = buffer[FieldLocations.VersionMadeByCompatibility],
VersionNeededToExtract = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.VersionNeededToExtract..]),
GeneralPurposeBitFlag = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.GeneralPurposeBitFlags..]),
CompressionMethod = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.CompressionMethod..]),
LastModified = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.LastModified..]),
Crc32 = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.Crc32..]),
FilenameLength = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.FilenameLength..]),
ExtraFieldLength = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.ExtraFieldLength..]),
FileCommentLength = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.FileCommentLength..]),
InternalFileAttributes = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.InternalFileAttributes..]),
ExternalFileAttributes = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.ExternalFileAttributes..])
};

uint compressedSizeSmall = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.CompressedSize..]);
uint uncompressedSizeSmall = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.UncompressedSize..]);

header.FilenameLength = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.FilenameLength..]);
header.ExtraFieldLength = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.ExtraFieldLength..]);
header.FileCommentLength = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.FileCommentLength..]);

ushort diskNumberStartSmall = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.DiskNumberStart..]);

header.InternalFileAttributes = BinaryPrimitives.ReadUInt16LittleEndian(buffer[FieldLocations.InternalFileAttributes..]);
header.ExternalFileAttributes = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.ExternalFileAttributes..]);

uint relativeOffsetOfLocalHeaderSmall = BinaryPrimitives.ReadUInt32LittleEndian(buffer[FieldLocations.RelativeOffsetOfLocalHeader..]);

// Assemble the dynamic header in a separate buffer. We can't guarantee that it's all in the input buffer,
Expand Down Expand Up @@ -770,14 +770,7 @@ internal sealed partial class ZipEndOfCentralDirectoryBlock
public uint SizeOfCentralDirectory;
public uint OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
private byte[]? _archiveComment;
public byte[] ArchiveComment
{
get
{
_archiveComment ??= [];
return _archiveComment;
}
}
public byte[] ArchiveComment => _archiveComment ??= [];

public static void WriteBlock(Stream stream, long numberOfEntries, long startOfCentralDirectory, long sizeOfCentralDirectory, byte[] archiveComment)
{
Expand Down