Please use System.IO.Compression! A fork of the DotNetZip project without signing with a solution that compiles cleanly. This project aims to follow semver to avoid versioning conflicts. DotNetZip is a FAST, FREE class library and toolset for manipulating zip files. Use VB, C# or any .NET language to easily create, extract, or update zip files.
Other
545
stars
218
forks
source link
Zip64 writes incorrect central directory file header/extended information extra field #260
I noticed that some of my files couldn't be extracted properly using System.IO.Compression, and after investigating it seems that it discards the extra field for being of unexpected size. This affects anything zip64; which is practically either zip files above 4gb size, files inside the zip file above 4gb size and/or files above the 4gb boundary (where the relative offsets cannot be represented anymore in just 4 bytes).
Other tools (such as recent 7zip) also note inconsistencies, but they often just show warnings or ignore the issue altogether; and read the file/extract it correctly anyways.
To reproduce, simply create a zip file and force zip64 to be used (the actual size doesn't matter for this, since it has the same issue as large files):
// <PackageReference Include="DotNetZip" Version="1.16.0" />
using DNZip = Ionic.Zip.ZipFile;
using SICZip = System.IO.Compression.ZipFile;
const string targetZip = "test.zip";
if (!File.Exists(targetZip))
{
using var czip = new DNZip { UseZip64WhenSaving = Ionic.Zip.Zip64Option.Always };
czip.AddEntry("SingleFile", (fn, s) => s.WriteByte(0));
czip.Save(targetZip);
}
using var zip = SICZip.OpenRead(targetZip);
var singleFile = zip.Entries.First();
_ = singleFile.CompressedLength; // this is returned as 0x0000_0000_ffff_ffff because the extra field is ignored
//using var _ = singleFile.Open(); // throws System.IO.InvalidDataException (A local file header is corrupt)
//SICZip.ExtractToDirectory(targetZip, "test"); // throws System.IO.InvalidDataException (A local file header is corrupt)
When staring at the file (for almost too long), it turns out that the compressed, uncompressed file size and relative header offset are set to 0xffff_ffff in the central directory file header; but the extra field has size 0x1c (28, which means it includes the disk start number). This extra field repeats the disk start number as zero (which is not set to 0xffff in the central directory file header).
They expect that extra field to be 0x18 (24), and ignore it altogether.
This was since addressed by dotnet/runtime#68106 to fix dotnet/runtime#49580; but it doesn't seem to have landed in any public release yet (at least none that aren't previews).
DotNetZip on the other hand should either:
set the central directory file header field for the disk start number to 0xffff and keep the extra field as-is, or
don't write the disk start number to the extra field.
I noticed that some of my files couldn't be extracted properly using
System.IO.Compression
, and after investigating it seems that it discards the extra field for being of unexpected size. This affects anything zip64; which is practically either zip files above 4gb size, files inside the zip file above 4gb size and/or files above the 4gb boundary (where the relative offsets cannot be represented anymore in just 4 bytes).Other tools (such as recent 7zip) also note inconsistencies, but they often just show warnings or ignore the issue altogether; and read the file/extract it correctly anyways.
To reproduce, simply create a zip file and force zip64 to be used (the actual size doesn't matter for this, since it has the same issue as large files):
When staring at the file (for almost too long), it turns out that the compressed, uncompressed file size and relative header offset are set to
0xffff_ffff
in the central directory file header; but the extra field has size0x1c
(28
, which means it includes the disk start number). This extra field repeats the disk start number as zero (which is not set to0xffff
in the central directory file header).All but the most recent version of
System.IO.Compression
have this piece of validation code that requires the fields to conform to the PKWare specification (which must have the disk start number as0xffff
in the regular record, or omit it from the extra field): https://github.com/dotnet/runtime/blob/83adfae6a6273d8fb4c69554aa3b1cc7cbf01c71/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.cs#L205-L206They expect that extra field to be
0x18
(24
), and ignore it altogether.This was since addressed by dotnet/runtime#68106 to fix dotnet/runtime#49580; but it doesn't seem to have landed in any public release yet (at least none that aren't previews).
DotNetZip on the other hand should either:
0xffff
and keep the extra field as-is, or