Merge pull request #1479 from wleeaf/zip-utf8-name-flag

Set the UTF-8 flag for zip entry names encoded as UTF-8 without a BOM
This commit is contained in:
Adam Hathcock
2026-10-27 13:47:53 +00:00
committed by GitHub
3 changed files with 38 additions and 6 deletions

View File

@@ -104,9 +104,10 @@ internal class ZipCentralDirectoryEntry
version = 20;
}
var flags = Equals(archiveEncoding.GetEncoding(), Encoding.UTF8)
? HeaderFlags.Efs
: HeaderFlags.None;
var flags =
archiveEncoding.GetEncoding().CodePage == Encoding.UTF8.CodePage
? HeaderFlags.Efs
: HeaderFlags.None;
if (UsesDataDescriptor)
{
flags |= HeaderFlags.UsePostDataDescriptor;

View File

@@ -207,9 +207,12 @@ public partial class ZipWriter : AbstractWriter
{
stream.Write(stackalloc byte[] { 63, 0 }); //version says we used PPMd or LZMA
}
var flags = Equals(WriterOptions.ArchiveEncoding.GetEncoding(), Encoding.UTF8)
? HeaderFlags.Efs
: 0;
// Compare code pages: Encoding.Default on .NET Core is a UTF-8 encoding without a BOM,
// which isn't Equal to Encoding.UTF8.
var flags =
WriterOptions.ArchiveEncoding.GetEncoding().CodePage == Encoding.UTF8.CodePage
? HeaderFlags.Efs
: 0;
if (usesDataDescriptor)
{
flags |= HeaderFlags.UsePostDataDescriptor;

View File

@@ -1,3 +1,5 @@
using System;
using System.Buffers.Binary;
using System.IO;
using System.Text;
using SharpCompress.Common;
@@ -47,6 +49,32 @@ public class ZipWriterTests : WriterTests
Assert.True(memoryStream.Length > 0);
}
[Fact]
public void Zip_Write_Utf8_Name_Sets_Efs_Flag()
{
// Encoding.Default is a UTF-8 encoding without a BOM on .NET Core
using var memoryStream = new MemoryStream();
var options = new WriterOptions(CompressionType.Deflate)
{
ArchiveEncoding = new ArchiveEncoding { Default = new UTF8Encoding(false) },
};
using (var writer = WriterFactory.OpenWriter(memoryStream, ArchiveType.Zip, options))
{
writer.Write("d\u00e9j\u00e0-vu.txt", new MemoryStream(new byte[] { 1, 2, 3 }));
}
var zip = memoryStream.ToArray();
var central = BinaryPrimitives.ReadInt32LittleEndian(zip.AsSpan(zip.Length - 6));
// Bit 11 of the general purpose flags marks the name as UTF-8 for other readers
Assert.Equal(0x800, BinaryPrimitives.ReadUInt16LittleEndian(zip.AsSpan(6)) & 0x800);
Assert.Equal(
0x800,
BinaryPrimitives.ReadUInt16LittleEndian(zip.AsSpan(central + 8)) & 0x800
);
}
[Fact]
public void Zip_Deflate_Write() =>
Write(