[PR #1023] [MERGED] Fix version mismatch between Local File Header and Central Directory File Header in Zip archives #1443

Open
opened 2026-01-29 22:20:36 +00:00 by claunia · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/adamhathcock/sharpcompress/pull/1023
Author: @Copilot
Created: 11/19/2025
Status: Merged
Merged: 11/25/2025
Merged by: @adamhathcock

Base: masterHead: copilot/fix-zip64-validation-issue


📝 Commits (9)

  • 51237a3 Initial plan
  • 434ce05 Fix Zip64 version mismatch between LFH and CDFH
  • 0fe48c6 Enhance fix to handle LZMA/PPMd/BZip2/ZStandard compression methods
  • 8b0a1c6 Update tests/SharpCompress.Test/Zip/Zip64VersionConsistencyTests.cs
  • cc10a12 Update tests/SharpCompress.Test/Zip/Zip64VersionConsistencyTests.cs
  • 04c044c Update tests/SharpCompress.Test/Zip/Zip64VersionConsistencyTests.cs
  • 81ca15b Update src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs
  • d41908a fixes for clarity
  • a92aaa5 Remove ZipCompressionMethod.None from version 63 check

📊 Changes

3 files changed (+466 additions, -2 deletions)

View changed files

📝 src/SharpCompress/Archives/IArchiveEntryExtensions.cs (+2 -1)
📝 src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs (+23 -1)
tests/SharpCompress.Test/Zip/Zip64VersionConsistencyTests.cs (+441 -0)

📄 Description

Plan: Fix Zip64 Version Mismatch Between LFH and CDFH

Problem

When UseZip64=true but files are small (< 4GB), there's a version mismatch:

  • Local File Header (LFH) writes version 45 (Zip64) or 63 (LZMA/PPMd)
  • Central Directory File Header (CDFH) writes version 20 (because actual sizes don't require Zip64)
  • This causes validation failures in System.IO.Packaging and potentially other readers

Root Cause

  • In ZipWriter.WriteHeader(): Version is set based on compression method and Zip64 usage
  • In ZipCentralDirectoryEntry.Write(): Version was set to 45 only when actual sizes/offsets exceed uint.MaxValue, ignoring compression method requirements

Solution

  • Understand the issue and existing code
  • Create tests to validate version consistency (5 comprehensive tests)
  • Fix ZipCentralDirectoryEntry.Write() to properly handle:
    • Version 63 for LZMA/PPMd/BZip2/ZStandard compression
    • Version 45 when Zip64HeaderOffset != 0 or actual sizes require it
    • Version 20 for standard Deflate/None compression
  • Run tests to ensure no regression (all 374 Zip tests pass)
  • Format code with CSharpier
  • Remove ZipCompressionMethod.None from version 63 check (per review feedback)

Changes Made

  1. Modified ZipCentralDirectoryEntry.cs:

    • Updated version calculation to check compression method first
    • Then check if Zip64 was used in LFH (Zip64HeaderOffset != 0)
    • Finally fallback to version 20 for standard compression
  2. Added Zip64VersionConsistencyTests.cs: Created 5 tests

    • Test with small file and UseZip64=true
    • Test with small file and UseZip64=false
    • Test with multiple small files and UseZip64=true
    • Test with LZMA compression (version 63)
    • Test with PPMd compression (version 63)

Test Results

  • All 5 new tests pass
  • All 374 Zip tests pass (including LZMA/PPMd)

This ensures archives created with UseZip64=true or advanced compression methods can be opened by System.IO.Packaging and other strict ZIP readers.

Original prompt

This section details on the original issue you should resolve

<issue_title>Failed validation when using Zip64 with System.IO.Packaging</issue_title>
<issue_description>Hi there, ran into this little problem when using SharpCompress.

A zip archive created with UseZip64 set to true will fail to open using ZipPackage (from System.IO.Packaging) with .NET Framework 4.8.

The exception occurs in ZipIOLocalFileBlock.Validate() here:

if (VersionNeededToExtract != centralDirFileHeader.VersionNeededToExtract || GeneralPurposeBitFlag != centralDirFileHeader.GeneralPurposeBitFlag || CompressedSize != centralDirFileHeader.CompressedSize || UncompressedSize != centralDirFileHeader.UncompressedSize || CompressionMethod != centralDirFileHeader.CompressionMethod || Crc32 != centralDirFileHeader.Crc32)
{
	throw new FileFormatException(SR.Get("CorruptedData"));
}

VersionNeededToExtract value is 45 while centralDirFileHeader.VersionNeededToExtract is 20.
This mismatch seems to come from the fact that in SharpCompress the Local File Header will always have its version set to 45 when UseZip64 is true, but the version in the Central Directory File Header will be 45 only if size/offset is over uint.MaxValue (which makes sense).

Not sure what's the best way to handle this, an easy one would probably be to set both versions to 45 as soon as the flag is true ? (but this may not be optimal/trivial).

In SharpCompress these parts seem to be in ZipWriter.WriteToStream() / ZipWriter.WriteHeader() for the LFH and in ZipCentralDirectoryEntry.Write() for the CDFH.

Small code to reproduce the issue:

using System.IO.Packaging;

using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.Writers;
using SharpCompress.Writers.Zip;

WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate)
{
    LeaveStreamOpen = false,
    UseZip64 = true
};
string file = "test_zip64.zip";

ZipArchive zipArchive = ZipArchive.Create();
zipArchive.AddEntry("empty", new MemoryStream());
zipArchive.SaveTo(file, writerOptions);

using var package = ZipPackage.Open(file, FileMode.Open, FileAccess.Read);

Kind of related to adamhathcock/sharpcompress#164</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/adamhathcock/sharpcompress/pull/1023 **Author:** [@Copilot](https://github.com/apps/copilot-swe-agent) **Created:** 11/19/2025 **Status:** ✅ Merged **Merged:** 11/25/2025 **Merged by:** [@adamhathcock](https://github.com/adamhathcock) **Base:** `master` ← **Head:** `copilot/fix-zip64-validation-issue` --- ### 📝 Commits (9) - [`51237a3`](https://github.com/adamhathcock/sharpcompress/commit/51237a34eb3fd8cb322de39572ba5bd847d6aa99) Initial plan - [`434ce05`](https://github.com/adamhathcock/sharpcompress/commit/434ce054162027c0eda137d9130cf02db7290862) Fix Zip64 version mismatch between LFH and CDFH - [`0fe48c6`](https://github.com/adamhathcock/sharpcompress/commit/0fe48c647e3b4351841b02028c5c7f3525899302) Enhance fix to handle LZMA/PPMd/BZip2/ZStandard compression methods - [`8b0a1c6`](https://github.com/adamhathcock/sharpcompress/commit/8b0a1c699feec7d7cbf03b1f30ed0b44796646bb) Update tests/SharpCompress.Test/Zip/Zip64VersionConsistencyTests.cs - [`cc10a12`](https://github.com/adamhathcock/sharpcompress/commit/cc10a12fbc522dd73b488ea939f1a2b4165026f9) Update tests/SharpCompress.Test/Zip/Zip64VersionConsistencyTests.cs - [`04c044c`](https://github.com/adamhathcock/sharpcompress/commit/04c044cb2bc0b71c7710e71550a7640ea2e36304) Update tests/SharpCompress.Test/Zip/Zip64VersionConsistencyTests.cs - [`81ca15b`](https://github.com/adamhathcock/sharpcompress/commit/81ca15b5676eb0cbb697bf9da18ad41466e52409) Update src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs - [`d41908a`](https://github.com/adamhathcock/sharpcompress/commit/d41908adebed4880d727f3279850af0effdb99f4) fixes for clarity - [`a92aaa5`](https://github.com/adamhathcock/sharpcompress/commit/a92aaa51d524100d8edc9cb0030fb8929e21e0eb) Remove ZipCompressionMethod.None from version 63 check ### 📊 Changes **3 files changed** (+466 additions, -2 deletions) <details> <summary>View changed files</summary> 📝 `src/SharpCompress/Archives/IArchiveEntryExtensions.cs` (+2 -1) 📝 `src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs` (+23 -1) ➕ `tests/SharpCompress.Test/Zip/Zip64VersionConsistencyTests.cs` (+441 -0) </details> ### 📄 Description ## Plan: Fix Zip64 Version Mismatch Between LFH and CDFH ### Problem When `UseZip64=true` but files are small (< 4GB), there's a version mismatch: - Local File Header (LFH) writes version 45 (Zip64) or 63 (LZMA/PPMd) - Central Directory File Header (CDFH) writes version 20 (because actual sizes don't require Zip64) - This causes validation failures in System.IO.Packaging and potentially other readers ### Root Cause - In `ZipWriter.WriteHeader()`: Version is set based on compression method and Zip64 usage - In `ZipCentralDirectoryEntry.Write()`: Version was set to 45 only when actual sizes/offsets exceed uint.MaxValue, ignoring compression method requirements ### Solution - [x] Understand the issue and existing code - [x] Create tests to validate version consistency (5 comprehensive tests) - [x] Fix `ZipCentralDirectoryEntry.Write()` to properly handle: - Version 63 for LZMA/PPMd/BZip2/ZStandard compression - Version 45 when Zip64HeaderOffset != 0 or actual sizes require it - Version 20 for standard Deflate/None compression - [x] Run tests to ensure no regression (all 374 Zip tests pass) - [x] Format code with CSharpier - [x] Remove `ZipCompressionMethod.None` from version 63 check (per review feedback) ### Changes Made 1. **Modified `ZipCentralDirectoryEntry.cs`**: - Updated version calculation to check compression method first - Then check if Zip64 was used in LFH (Zip64HeaderOffset != 0) - Finally fallback to version 20 for standard compression 2. **Added `Zip64VersionConsistencyTests.cs`**: Created 5 tests - Test with small file and UseZip64=true - Test with small file and UseZip64=false - Test with multiple small files and UseZip64=true - Test with LZMA compression (version 63) - Test with PPMd compression (version 63) ### Test Results - ✅ All 5 new tests pass - ✅ All 374 Zip tests pass (including LZMA/PPMd) This ensures archives created with `UseZip64=true` or advanced compression methods can be opened by System.IO.Packaging and other strict ZIP readers. <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Failed validation when using Zip64 with System.IO.Packaging</issue_title> > <issue_description>Hi there, ran into this little problem when using SharpCompress. > > A zip archive created with `UseZip64` set to `true` will fail to open using `ZipPackage` (from `System.IO.Packaging`) with .NET Framework 4.8. > > The exception occurs in `ZipIOLocalFileBlock.Validate()` here: > ```cs > if (VersionNeededToExtract != centralDirFileHeader.VersionNeededToExtract || GeneralPurposeBitFlag != centralDirFileHeader.GeneralPurposeBitFlag || CompressedSize != centralDirFileHeader.CompressedSize || UncompressedSize != centralDirFileHeader.UncompressedSize || CompressionMethod != centralDirFileHeader.CompressionMethod || Crc32 != centralDirFileHeader.Crc32) > { > throw new FileFormatException(SR.Get("CorruptedData")); > } > ``` > > `VersionNeededToExtract` value is `45` while `centralDirFileHeader.VersionNeededToExtract` is `20`. > This mismatch seems to come from the fact that in SharpCompress the Local File Header will always have its version set to `45` when `UseZip64` is `true`, but the version in the Central Directory File Header will be `45` only if size/offset is over `uint.MaxValue` (which makes sense). > > Not sure what's the best way to handle this, an easy one would probably be to set both versions to `45` as soon as the flag is `true` ? (but this may not be optimal/trivial). > > In SharpCompress these parts seem to be in `ZipWriter.WriteToStream()` / `ZipWriter.WriteHeader()` for the LFH and in `ZipCentralDirectoryEntry.Write()` for the CDFH. > > Small code to reproduce the issue: > ```cs > using System.IO.Packaging; > > using SharpCompress.Archives; > using SharpCompress.Archives.Zip; > using SharpCompress.Common; > using SharpCompress.Writers; > using SharpCompress.Writers.Zip; > > WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate) > { > LeaveStreamOpen = false, > UseZip64 = true > }; > string file = "test_zip64.zip"; > > ZipArchive zipArchive = ZipArchive.Create(); > zipArchive.AddEntry("empty", new MemoryStream()); > zipArchive.SaveTo(file, writerOptions); > > using var package = ZipPackage.Open(file, FileMode.Open, FileAccess.Read); > ``` > > Kind of related to adamhathcock/sharpcompress#164</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> - Fixes adamhathcock/sharpcompress#1022 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
claunia added the pull-request label 2026-01-29 22:20:36 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/sharpcompress#1443