diff --git a/src/libraries/Common/tests/System/IO/Compression/ZipTestHelper.cs b/src/libraries/Common/tests/System/IO/Compression/ZipTestHelper.cs index b5937a946869e3..9308fe1a305018 100644 --- a/src/libraries/Common/tests/System/IO/Compression/ZipTestHelper.cs +++ b/src/libraries/Common/tests/System/IO/Compression/ZipTestHelper.cs @@ -388,6 +388,7 @@ internal static void AddEntry(ZipArchive archive, string name, string contents, protected const string Utf8LowerCaseOUmlautChar = "\u00F6"; protected const string Utf8CopyrightChar = "\u00A9"; protected const string AsciiFileName = "file.txt"; + protected const string UnicodeFileName = "\u4f60\u597D.txt"; // The o with umlaut is a character that exists in both latin1 and utf8 protected const string Utf8AndLatin1FileName = $"{Utf8LowerCaseOUmlautChar}.txt"; // emojis only make sense in utf8 diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs index 7bf3271886aea7..0e1fb42209594e 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs @@ -42,7 +42,6 @@ public partial class ZipArchiveEntry private List? _lhUnknownExtraFields; private byte[] _fileComment; private readonly CompressionLevel? _compressionLevel; - private bool _hasUnicodeEntryNameOrComment; // Initializes a ZipArchiveEntry instance for an existing archive entry. internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd) @@ -84,8 +83,6 @@ internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd) _fileComment = cd.FileComment; _compressionLevel = null; - - _hasUnicodeEntryNameOrComment = (_generalPurposeBitFlag & BitFlagValues.UnicodeFileNameAndComment) != 0; } // Initializes a ZipArchiveEntry instance for a new archive entry with a specified compression level. @@ -144,8 +141,6 @@ internal ZipArchiveEntry(ZipArchive archive, string entryName) { _archive.AcquireArchiveStream(this); } - - _hasUnicodeEntryNameOrComment = false; } /// @@ -197,7 +192,11 @@ public string Comment set { _fileComment = ZipHelper.GetEncodedTruncatedBytesFromString(value, _archive.EntryNameAndCommentEncoding, ushort.MaxValue, out bool isUTF8); - _hasUnicodeEntryNameOrComment |= isUTF8; + + if (isUTF8) + { + _generalPurposeBitFlag |= BitFlagValues.UnicodeFileNameAndComment; + } } } @@ -218,11 +217,19 @@ private set ArgumentNullException.ThrowIfNull(value, nameof(FullName)); _storedEntryNameBytes = ZipHelper.GetEncodedTruncatedBytesFromString( - value, _archive.EntryNameAndCommentEncoding, 0 /* No truncation */, out bool hasUnicodeEntryName); + value, _archive.EntryNameAndCommentEncoding, 0 /* No truncation */, out bool isUTF8); - _hasUnicodeEntryNameOrComment |= hasUnicodeEntryName; _storedEntryName = value; + if (isUTF8) + { + _generalPurposeBitFlag |= BitFlagValues.UnicodeFileNameAndComment; + } + else + { + _generalPurposeBitFlag &= ~BitFlagValues.UnicodeFileNameAndComment; + } + DetectEntryNameVersion(); } } @@ -505,11 +512,6 @@ internal void WriteCentralDirectoryFileHeader() extraFieldLength = (ushort)bigExtraFieldLength; } - if (_hasUnicodeEntryNameOrComment) - _generalPurposeBitFlag |= BitFlagValues.UnicodeFileNameAndComment; - else - _generalPurposeBitFlag &= ~BitFlagValues.UnicodeFileNameAndComment; - writer.Write(ZipCentralDirectoryFileHeader.SignatureConstant); // Central directory file header signature (4 bytes) writer.Write((byte)_versionMadeBySpecification); // Version made by Specification (version) (1 byte) writer.Write((byte)CurrentZipPlatform); // Version made by Compatibility (type) (1 byte) diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.cs index 64fffd2e1f945d..f85b1777bd7c82 100644 --- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.cs +++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.cs @@ -554,7 +554,7 @@ internal struct ZipEndOfCentralDirectoryBlock public uint OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber; public byte[] ArchiveComment; - public static void WriteBlock(Stream stream, long numberOfEntries, long startOfCentralDirectory, long sizeOfCentralDirectory, byte[]? archiveComment) + public static void WriteBlock(Stream stream, long numberOfEntries, long startOfCentralDirectory, long sizeOfCentralDirectory, byte[] archiveComment) { BinaryWriter writer = new BinaryWriter(stream); @@ -574,10 +574,10 @@ public static void WriteBlock(Stream stream, long numberOfEntries, long startOfC writer.Write(startOfCentralDirectoryTruncated); // Should be valid because of how we read archiveComment in TryReadBlock: - Debug.Assert((archiveComment == null) || (archiveComment.Length <= ZipFileCommentMaxLength)); + Debug.Assert(archiveComment.Length <= ZipFileCommentMaxLength); - writer.Write(archiveComment != null ? (ushort)archiveComment.Length : (ushort)0); // zip file comment length - if (archiveComment != null) + writer.Write((ushort)archiveComment.Length); // zip file comment length + if (archiveComment.Length > 0) writer.Write(archiveComment); } diff --git a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_CreateTests.cs b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_CreateTests.cs index a964f5da7587f1..bf84eb4288c7e5 100644 --- a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_CreateTests.cs +++ b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_CreateTests.cs @@ -176,6 +176,21 @@ public static void CreateNormal_VerifyDataDescriptor() AssertDataDescriptor(memoryStream, false); } + [Theory] + [InlineData(UnicodeFileName, UnicodeFileName, true)] + [InlineData(UnicodeFileName, AsciiFileName, true)] + [InlineData(AsciiFileName, UnicodeFileName, true)] + [InlineData(AsciiFileName, AsciiFileName, false)] + public static void CreateNormal_VerifyUnicodeFileNameAndComment(string fileName, string entryComment, bool isUnicodeFlagExpected) + { + using var ms = new MemoryStream(); + using var archive = new ZipArchive(ms, ZipArchiveMode.Create); + + CreateEntry(archive, fileName, fileContents: "xxx", entryComment); + + AssertUnicodeFileNameAndComment(ms, isUnicodeFlagExpected); + } + [Fact] public static void CreateNormal_With2SameEntries_ThrowException() { @@ -198,11 +213,12 @@ private static string ReadStringFromSpan(Span input) return Text.Encoding.UTF8.GetString(input.ToArray()); } - private static void CreateEntry(ZipArchive archive, string fileName, string fileContents) + private static void CreateEntry(ZipArchive archive, string fileName, string fileContents, string entryComment = null) { ZipArchiveEntry entry = archive.CreateEntry(fileName); using StreamWriter writer = new StreamWriter(entry.Open()); writer.Write(fileContents); + entry.Comment = entryComment; } private static void AssertDataDescriptor(MemoryStream memoryStream, bool hasDataDescriptor) @@ -211,5 +227,12 @@ private static void AssertDataDescriptor(MemoryStream memoryStream, bool hasData Assert.Equal(hasDataDescriptor ? 8 : 0, fileBytes[6]); Assert.Equal(0, fileBytes[7]); } + + private static void AssertUnicodeFileNameAndComment(MemoryStream memoryStream, bool isUnicodeFlagExpected) + { + byte[] fileBytes = memoryStream.ToArray(); + Assert.Equal(0, fileBytes[6]); + Assert.Equal(isUnicodeFlagExpected ? 8 : 0, fileBytes[7]); + } } }