Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set version in ZIP local header to ZIP64, when the file offset is >4GB #94970

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 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 @@ -964,7 +964,7 @@ private void WriteCrcAndSizesInLocalHeader(bool zip64HeaderUsed)
long finalPosition = _archive.ArchiveStream.Position;
BinaryWriter writer = new BinaryWriter(_archive.ArchiveStream);

bool zip64Needed = SizesTooLarge()
bool zip64Needed = SizesTooLarge() || (_offsetOfLocalHeader > uint.MaxValue)
karakasa marked this conversation as resolved.
Show resolved Hide resolved
#if DEBUG_FORCE_ZIP64
|| _archive._forceZip64
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Xunit;

namespace System.IO.Compression.Tests
Expand Down Expand Up @@ -44,5 +45,81 @@ public static void UnzipOver4GBZipFile()
tempDir.Delete(recursive: true);
}
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsSpeedOptimized), nameof(PlatformDetection.Is64BitProcess))] // don't run it on slower runtimes
[OuterLoop("It requires 5 GB of free disk space")]
public static void CheckZIP64VersionIsSet_ForSmallFilesAfterBigFiles()
{
// issue #94899

byte[] largeBuffer = GC.AllocateUninitializedArray<byte>(1_000_000_000); // 1 GB
byte[] smallBuffer = GC.AllocateUninitializedArray<byte>(1000);

string zipArchivePath = Path.Combine(Path.GetTempPath(), "over4GB.zip");
DirectoryInfo tempDir = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "over4GB"));

try
{
using FileStream fs = File.Open(zipArchivePath, FileMode.Create, FileAccess.ReadWrite);
const string LargeFileName = "largefile";
const string SmallFileName = "smallfile";
const uint ZipLocalFileHeader_OffsetToVersionFromHeaderStart = 4;
const ushort Zip64Version = 45;

{
// Create

using var archive = new ZipArchive(fs, ZipArchiveMode.Create, true);
ZipArchiveEntry file = archive.CreateEntry(LargeFileName, CompressionLevel.NoCompression);
karakasa marked this conversation as resolved.
Show resolved Hide resolved

using (Stream stream = file.Open())
{
// Write 5GB of data

stream.Write(largeBuffer);
stream.Write(largeBuffer);
stream.Write(largeBuffer);
stream.Write(largeBuffer);
stream.Write(largeBuffer);
}

file = archive.CreateEntry(SmallFileName, CompressionLevel.NoCompression);

using (Stream stream = file.Open())
{
stream.Write(smallBuffer);
}
}

fs.Position = 0;

{
// Validate

using var reader = new BinaryReader(fs);
using var archive = new ZipArchive(fs, ZipArchiveMode.Read);
FieldInfo offsetOfLHField = typeof(ZipArchiveEntry).GetField("_offsetOfLocalHeader", BindingFlags.NonPublic | BindingFlags.Instance);

if (offsetOfLHField is null || offsetOfLHField.FieldType != typeof(long))
{
Assert.Fail("Cannot find the private field of _offsetOfLocalHeader in ZipArchiveEntry or the type is not long. Code may be changed after the test is written.");
}

foreach (ZipArchiveEntry entry in archive.Entries)
{
fs.Position = (long)offsetOfLHField.GetValue(entry) + ZipLocalFileHeader_OffsetToVersionFromHeaderStart;
ushort versionNeeded = reader.ReadUInt16();

Assert.True(versionNeeded >= Zip64Version, "ZIP64 version is not set for files with LH at >4GB offset.");
karakasa marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
finally
{
File.Delete(zipArchivePath);

tempDir.Delete(recursive: true);
karakasa marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}