@@ -43,7 +43,9 @@ public partial class ZipArchiveEntry
43
43
private byte [ ] _storedEntryNameBytes ;
44
44
// only apply to update mode
45
45
private List < ZipGenericExtraField > ? _cdUnknownExtraFields ;
46
+ private byte [ ] ? _cdTrailingExtraFieldData ;
46
47
private List < ZipGenericExtraField > ? _lhUnknownExtraFields ;
48
+ private byte [ ] ? _lhTrailingExtraFieldData ;
47
49
private byte [ ] _fileComment ;
48
50
private readonly CompressionLevel _compressionLevel ;
49
51
@@ -53,6 +55,11 @@ internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd)
53
55
_archive = archive ;
54
56
55
57
_originallyInArchive = true ;
58
+ // It's possible for the CompressionMethod setter and DetectEntryNameVersion to update this, even without any explicit
59
+ // changes. This can occur if a ZipArchive instance runs in Update mode and opens a stream with invalid data. In such
60
+ // a situation, both the local file header and the central directory header will be rewritten (to prevent the headers
61
+ // from falling out of sync when the central directory header is rewritten.)
62
+ Changes = ZipArchive . ChangeState . Unchanged ;
56
63
57
64
_diskNumberStart = cd . DiskNumberStart ;
58
65
_versionMadeByPlatform = ( ZipVersionMadeByPlatform ) cd . VersionMadeByCompatibility ;
@@ -84,12 +91,11 @@ internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd)
84
91
_lhUnknownExtraFields = null ;
85
92
// the cd should have this as null if we aren't in Update mode
86
93
_cdUnknownExtraFields = cd . ExtraFields ;
94
+ _cdTrailingExtraFieldData = cd . TrailingExtraFieldData ;
87
95
88
96
_fileComment = cd . FileComment ;
89
97
90
98
_compressionLevel = MapCompressionLevel ( _generalPurposeBitFlag , CompressionMethod ) ;
91
-
92
- Changes = ZipArchive . ChangeState . Unchanged ;
93
99
}
94
100
95
101
// Initializes a ZipArchiveEntry instance for a new archive entry with a specified compression level.
@@ -543,8 +549,9 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite)
543
549
544
550
545
551
// determine if we can fit zip64 extra field and original extra fields all in
552
+ int currExtraFieldDataLength = ZipGenericExtraField . TotalSize ( _cdUnknownExtraFields , _cdTrailingExtraFieldData ? . Length ?? 0 ) ;
546
553
int bigExtraFieldLength = ( zip64ExtraField != null ? zip64ExtraField . TotalSize : 0 )
547
- + ( _cdUnknownExtraFields != null ? ZipGenericExtraField . TotalSize ( _cdUnknownExtraFields ) : 0 ) ;
554
+ + currExtraFieldDataLength ;
548
555
ushort extraFieldLength ;
549
556
if ( bigExtraFieldLength > ushort . MaxValue )
550
557
{
@@ -561,7 +568,7 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite)
561
568
long centralDirectoryHeaderLength = ZipCentralDirectoryFileHeader . FieldLocations . DynamicData
562
569
+ _storedEntryNameBytes . Length
563
570
+ ( zip64ExtraField != null ? zip64ExtraField . TotalSize : 0 )
564
- + ( _cdUnknownExtraFields != null ? ZipGenericExtraField . TotalSize ( _cdUnknownExtraFields ) : 0 )
571
+ + currExtraFieldDataLength
565
572
+ _fileComment . Length ;
566
573
567
574
_archive . ArchiveStream . Seek ( centralDirectoryHeaderLength , SeekOrigin . Current ) ;
@@ -609,13 +616,11 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite)
609
616
_archive . ArchiveStream . Write ( cdStaticHeader ) ;
610
617
_archive . ArchiveStream . Write ( _storedEntryNameBytes ) ;
611
618
612
- // write extra fields, and only write zip64ExtraField if we decided we need it (it's not null)
619
+ // only write zip64ExtraField if we decided we need it (it's not null)
613
620
zip64ExtraField ? . WriteBlock ( _archive . ArchiveStream ) ;
614
621
615
- if ( _cdUnknownExtraFields != null )
616
- {
617
- ZipGenericExtraField . WriteAllBlocks ( _cdUnknownExtraFields , _archive . ArchiveStream ) ;
618
- }
622
+ // write extra fields (and any malformed trailing data).
623
+ ZipGenericExtraField . WriteAllBlocks ( _cdUnknownExtraFields , _cdTrailingExtraFieldData ?? Array . Empty < byte > ( ) , _archive . ArchiveStream ) ;
619
624
620
625
if ( _fileComment . Length > 0 )
621
626
{
@@ -626,7 +631,7 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite)
626
631
627
632
// throws exception if fails, will get called on every relevant entry before closing in update mode
628
633
// can throw InvalidDataException
629
- internal void LoadLocalHeaderExtraFieldAndCompressedBytesIfNeeded ( )
634
+ internal void LoadLocalHeaderExtraFieldIfNeeded ( )
630
635
{
631
636
// we should have made this exact call in _archive.Init through ThrowIfOpenable
632
637
Debug . Assert ( IsOpenable ( false , true , out _ ) ) ;
@@ -635,8 +640,16 @@ internal void LoadLocalHeaderExtraFieldAndCompressedBytesIfNeeded()
635
640
if ( _originallyInArchive )
636
641
{
637
642
_archive . ArchiveStream . Seek ( _offsetOfLocalHeader , SeekOrigin . Begin ) ;
638
- _lhUnknownExtraFields = ZipLocalFileHeader . GetExtraFields ( _archive . ArchiveStream ) ;
643
+ _lhUnknownExtraFields = ZipLocalFileHeader . GetExtraFields ( _archive . ArchiveStream , out _lhTrailingExtraFieldData ) ;
639
644
}
645
+ }
646
+
647
+ // throws exception if fails, will get called on every relevant entry before closing in update mode
648
+ // can throw InvalidDataException
649
+ internal void LoadCompressedBytesIfNeeded ( )
650
+ {
651
+ // we should have made this exact call in _archive.Init through ThrowIfOpenable
652
+ Debug . Assert ( IsOpenable ( false , true , out _ ) ) ;
640
653
641
654
if ( ! _everOpenedForWrite && _originallyInArchive )
642
655
{
@@ -979,8 +992,9 @@ private bool WriteLocalFileHeader(bool isEmptyFile, bool forceWrite)
979
992
_offsetOfLocalHeader = _archive . ArchiveStream . Position ;
980
993
981
994
// calculate extra field. if zip64 stuff + original extraField aren't going to fit, dump the original extraField, because this is more important
995
+ int currExtraFieldDataLength = ZipGenericExtraField . TotalSize ( _lhUnknownExtraFields , _lhTrailingExtraFieldData ? . Length ?? 0 ) ;
982
996
int bigExtraFieldLength = ( zip64ExtraField != null ? zip64ExtraField . TotalSize : 0 )
983
- + ( _lhUnknownExtraFields != null ? ZipGenericExtraField . TotalSize ( _lhUnknownExtraFields ) : 0 ) ;
997
+ + currExtraFieldDataLength ;
984
998
ushort extraFieldLength ;
985
999
if ( bigExtraFieldLength > ushort . MaxValue )
986
1000
{
@@ -1003,10 +1017,7 @@ private bool WriteLocalFileHeader(bool isEmptyFile, bool forceWrite)
1003
1017
_archive . ArchiveStream . Seek ( zip64ExtraField . TotalSize , SeekOrigin . Current ) ;
1004
1018
}
1005
1019
1006
- if ( _lhUnknownExtraFields != null )
1007
- {
1008
- _archive . ArchiveStream . Seek ( ZipGenericExtraField . TotalSize ( _lhUnknownExtraFields ) , SeekOrigin . Current ) ;
1009
- }
1020
+ _archive . ArchiveStream . Seek ( currExtraFieldDataLength , SeekOrigin . Current ) ;
1010
1021
}
1011
1022
else
1012
1023
{
@@ -1029,8 +1040,7 @@ private bool WriteLocalFileHeader(bool isEmptyFile, bool forceWrite)
1029
1040
// Only when handling zip64
1030
1041
zip64ExtraField ? . WriteBlock ( _archive . ArchiveStream ) ;
1031
1042
1032
- if ( _lhUnknownExtraFields != null )
1033
- ZipGenericExtraField . WriteAllBlocks ( _lhUnknownExtraFields , _archive . ArchiveStream ) ;
1043
+ ZipGenericExtraField . WriteAllBlocks ( _lhUnknownExtraFields , _lhTrailingExtraFieldData ?? Array . Empty < byte > ( ) , _archive . ArchiveStream ) ;
1034
1044
}
1035
1045
1036
1046
return zip64ExtraField != null ;
@@ -1252,10 +1262,12 @@ private void VersionToExtractAtLeast(ZipVersionNeededValues value)
1252
1262
if ( _versionToExtract < value )
1253
1263
{
1254
1264
_versionToExtract = value ;
1265
+ Changes |= ZipArchive . ChangeState . FixedLengthMetadata ;
1255
1266
}
1256
1267
if ( _versionMadeBySpecification < value )
1257
1268
{
1258
1269
_versionMadeBySpecification = value ;
1270
+ Changes |= ZipArchive . ChangeState . FixedLengthMetadata ;
1259
1271
}
1260
1272
}
1261
1273
0 commit comments