Skip to content

Commit 83adfae

Browse files
authored
Fix ZipArchiveEntry names shown as corrupted on other zip programs (#65886)
* Fix ZipArchiveEntry names shown as corrupted on other zip programs * Add test
1 parent 73471b5 commit 83adfae

File tree

4 files changed

+44
-18
lines changed

4 files changed

+44
-18
lines changed

src/libraries/Common/tests/System/IO/Compression/ZipTestHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ internal static void AddEntry(ZipArchive archive, string name, string contents,
388388
protected const string Utf8LowerCaseOUmlautChar = "\u00F6";
389389
protected const string Utf8CopyrightChar = "\u00A9";
390390
protected const string AsciiFileName = "file.txt";
391+
protected const string UnicodeFileName = "\u4f60\u597D.txt";
391392
// The o with umlaut is a character that exists in both latin1 and utf8
392393
protected const string Utf8AndLatin1FileName = $"{Utf8LowerCaseOUmlautChar}.txt";
393394
// emojis only make sense in utf8

src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ public partial class ZipArchiveEntry
4242
private List<ZipGenericExtraField>? _lhUnknownExtraFields;
4343
private byte[] _fileComment;
4444
private readonly CompressionLevel? _compressionLevel;
45-
private bool _hasUnicodeEntryNameOrComment;
4645

4746
// Initializes a ZipArchiveEntry instance for an existing archive entry.
4847
internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd)
@@ -84,8 +83,6 @@ internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd)
8483
_fileComment = cd.FileComment;
8584

8685
_compressionLevel = null;
87-
88-
_hasUnicodeEntryNameOrComment = (_generalPurposeBitFlag & BitFlagValues.UnicodeFileNameAndComment) != 0;
8986
}
9087

9188
// Initializes a ZipArchiveEntry instance for a new archive entry with a specified compression level.
@@ -144,8 +141,6 @@ internal ZipArchiveEntry(ZipArchive archive, string entryName)
144141
{
145142
_archive.AcquireArchiveStream(this);
146143
}
147-
148-
_hasUnicodeEntryNameOrComment = false;
149144
}
150145

151146
/// <summary>
@@ -197,7 +192,11 @@ public string Comment
197192
set
198193
{
199194
_fileComment = ZipHelper.GetEncodedTruncatedBytesFromString(value, _archive.EntryNameAndCommentEncoding, ushort.MaxValue, out bool isUTF8);
200-
_hasUnicodeEntryNameOrComment |= isUTF8;
195+
196+
if (isUTF8)
197+
{
198+
_generalPurposeBitFlag |= BitFlagValues.UnicodeFileNameAndComment;
199+
}
201200
}
202201
}
203202

@@ -218,11 +217,19 @@ private set
218217
ArgumentNullException.ThrowIfNull(value, nameof(FullName));
219218

220219
_storedEntryNameBytes = ZipHelper.GetEncodedTruncatedBytesFromString(
221-
value, _archive.EntryNameAndCommentEncoding, 0 /* No truncation */, out bool hasUnicodeEntryName);
220+
value, _archive.EntryNameAndCommentEncoding, 0 /* No truncation */, out bool isUTF8);
222221

223-
_hasUnicodeEntryNameOrComment |= hasUnicodeEntryName;
224222
_storedEntryName = value;
225223

224+
if (isUTF8)
225+
{
226+
_generalPurposeBitFlag |= BitFlagValues.UnicodeFileNameAndComment;
227+
}
228+
else
229+
{
230+
_generalPurposeBitFlag &= ~BitFlagValues.UnicodeFileNameAndComment;
231+
}
232+
226233
DetectEntryNameVersion();
227234
}
228235
}
@@ -505,11 +512,6 @@ internal void WriteCentralDirectoryFileHeader()
505512
extraFieldLength = (ushort)bigExtraFieldLength;
506513
}
507514

508-
if (_hasUnicodeEntryNameOrComment)
509-
_generalPurposeBitFlag |= BitFlagValues.UnicodeFileNameAndComment;
510-
else
511-
_generalPurposeBitFlag &= ~BitFlagValues.UnicodeFileNameAndComment;
512-
513515
writer.Write(ZipCentralDirectoryFileHeader.SignatureConstant); // Central directory file header signature (4 bytes)
514516
writer.Write((byte)_versionMadeBySpecification); // Version made by Specification (version) (1 byte)
515517
writer.Write((byte)CurrentZipPlatform); // Version made by Compatibility (type) (1 byte)

src/libraries/System.IO.Compression/src/System/IO/Compression/ZipBlocks.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ internal struct ZipEndOfCentralDirectoryBlock
554554
public uint OffsetOfStartOfCentralDirectoryWithRespectToTheStartingDiskNumber;
555555
public byte[] ArchiveComment;
556556

557-
public static void WriteBlock(Stream stream, long numberOfEntries, long startOfCentralDirectory, long sizeOfCentralDirectory, byte[]? archiveComment)
557+
public static void WriteBlock(Stream stream, long numberOfEntries, long startOfCentralDirectory, long sizeOfCentralDirectory, byte[] archiveComment)
558558
{
559559
BinaryWriter writer = new BinaryWriter(stream);
560560

@@ -574,10 +574,10 @@ public static void WriteBlock(Stream stream, long numberOfEntries, long startOfC
574574
writer.Write(startOfCentralDirectoryTruncated);
575575

576576
// Should be valid because of how we read archiveComment in TryReadBlock:
577-
Debug.Assert((archiveComment == null) || (archiveComment.Length <= ZipFileCommentMaxLength));
577+
Debug.Assert(archiveComment.Length <= ZipFileCommentMaxLength);
578578

579-
writer.Write(archiveComment != null ? (ushort)archiveComment.Length : (ushort)0); // zip file comment length
580-
if (archiveComment != null)
579+
writer.Write((ushort)archiveComment.Length); // zip file comment length
580+
if (archiveComment.Length > 0)
581581
writer.Write(archiveComment);
582582
}
583583

src/libraries/System.IO.Compression/tests/ZipArchive/zip_CreateTests.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,21 @@ public static void CreateNormal_VerifyDataDescriptor()
176176
AssertDataDescriptor(memoryStream, false);
177177
}
178178

179+
[Theory]
180+
[InlineData(UnicodeFileName, UnicodeFileName, true)]
181+
[InlineData(UnicodeFileName, AsciiFileName, true)]
182+
[InlineData(AsciiFileName, UnicodeFileName, true)]
183+
[InlineData(AsciiFileName, AsciiFileName, false)]
184+
public static void CreateNormal_VerifyUnicodeFileNameAndComment(string fileName, string entryComment, bool isUnicodeFlagExpected)
185+
{
186+
using var ms = new MemoryStream();
187+
using var archive = new ZipArchive(ms, ZipArchiveMode.Create);
188+
189+
CreateEntry(archive, fileName, fileContents: "xxx", entryComment);
190+
191+
AssertUnicodeFileNameAndComment(ms, isUnicodeFlagExpected);
192+
}
193+
179194
[Fact]
180195
public static void CreateNormal_With2SameEntries_ThrowException()
181196
{
@@ -198,11 +213,12 @@ private static string ReadStringFromSpan(Span<byte> input)
198213
return Text.Encoding.UTF8.GetString(input.ToArray());
199214
}
200215

201-
private static void CreateEntry(ZipArchive archive, string fileName, string fileContents)
216+
private static void CreateEntry(ZipArchive archive, string fileName, string fileContents, string entryComment = null)
202217
{
203218
ZipArchiveEntry entry = archive.CreateEntry(fileName);
204219
using StreamWriter writer = new StreamWriter(entry.Open());
205220
writer.Write(fileContents);
221+
entry.Comment = entryComment;
206222
}
207223

208224
private static void AssertDataDescriptor(MemoryStream memoryStream, bool hasDataDescriptor)
@@ -211,5 +227,12 @@ private static void AssertDataDescriptor(MemoryStream memoryStream, bool hasData
211227
Assert.Equal(hasDataDescriptor ? 8 : 0, fileBytes[6]);
212228
Assert.Equal(0, fileBytes[7]);
213229
}
230+
231+
private static void AssertUnicodeFileNameAndComment(MemoryStream memoryStream, bool isUnicodeFlagExpected)
232+
{
233+
byte[] fileBytes = memoryStream.ToArray();
234+
Assert.Equal(0, fileBytes[6]);
235+
Assert.Equal(isUnicodeFlagExpected ? 8 : 0, fileBytes[7]);
236+
}
214237
}
215238
}

0 commit comments

Comments
 (0)