When opened for Write ZipArchiveEntry needs to write the local header
information to the archive stream upon the first call to write since the
compressed content comes after that and we write directly to the backing stream
rather than buffering in memory.
By default header information only accommodates 32-bit sizes since this is much
more likely and saves space in archives with lots of files and at the first
write we don’t know how big the entry is going to be.
Once the entry is disposed we know the final length and may find it cannot be
represented in 32-bit fields. It can’t insert the extra bytes it would need
in the header region since it’s already written the compressed data and
streams/files don’t have insertion operations, so instead it sets a bit that
says the entry's size information is in a descriptor at the end of the entry.
The entry was setting the bit that indicated the descriptor existed but didn't
set the version in the header to ZIP64 to indicate wether that descriptor had
64-bit fields. Additionally we were writing the central-directory header for
the file with a version of ZIP64 so our local header and central header didn't
match.
May ZIP implementations were OK with this, but not all. Specifically
WindowsBase.dll's implementation of the System.IO.Packaging APIs has a
ZipArchive that insists the local header version must be ZIP64 to have a
64-bit descriptor. Moreover it also insists that the local-header version must
match the central header version.
To ensure zips created with .NETCore's System.IO.Packaging APIs work with
desktop we should set this version field in the local header.
Commit migrated from https://github.com/dotnet/corefx/commit/
b37b4efe6b58eecb83b81e6c754bd5dbe506bedc
// first step is, if we need zip64, but didn't allocate it, pretend we did a stream write, because
// we can't go back and give ourselves the space that the extra field needs.
- // we do this by setting the correct property in the bit flag
+ // we do this by setting the correct property in the bit flag to indicate we have a data descriptor
+ // and setting the version to Zip64 to indicate that descriptor contains 64-bit values
if (pretendStreaming)
{
+ VersionToExtractAtLeast(ZipVersionNeededValues.Zip64);
_generalPurposeBitFlag |= BitFlagValues.DataDescriptor;
- _archive.ArchiveStream.Seek(_offsetOfLocalHeader + ZipLocalFileHeader.OffsetToBitFlagFromHeaderStart,
+ _archive.ArchiveStream.Seek(_offsetOfLocalHeader + ZipLocalFileHeader.OffsetToVersionFromHeaderStart,
SeekOrigin.Begin);
+ writer.Write((ushort)_versionToExtract);
writer.Write((ushort)_generalPurposeBitFlag);
}
SeekOrigin.Begin);
writer.Write(_uncompressedSize);
writer.Write(_compressedSize);
-
- _archive.ArchiveStream.Seek(finalPosition, SeekOrigin.Begin);
}
// now go to the where we were. assume that this is the end of the data
public const uint DataDescriptorSignature = 0x08074B50;
public const uint SignatureConstant = 0x04034B50;
public const int OffsetToCrcFromHeaderStart = 14;
+ public const int OffsetToVersionFromHeaderStart = 4;
public const int OffsetToBitFlagFromHeaderStart = 6;
public const int SizeOfLocalHeader = 30;