@@ -43,7 +43,9 @@ public partial class ZipArchiveEntry
4343        private  byte [ ]  _storedEntryNameBytes ; 
4444        // only apply to update mode 
4545        private  List < ZipGenericExtraField > ?  _cdUnknownExtraFields ; 
46+         private  byte [ ] ?  _cdTrailingExtraFieldData ; 
4647        private  List < ZipGenericExtraField > ?  _lhUnknownExtraFields ; 
48+         private  byte [ ] ?  _lhTrailingExtraFieldData ; 
4749        private  byte [ ]  _fileComment ; 
4850        private  readonly  CompressionLevel  _compressionLevel ; 
4951
@@ -53,6 +55,11 @@ internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd)
5355            _archive  =  archive ; 
5456
5557            _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 ; 
5663
5764            _diskNumberStart  =  cd . DiskNumberStart ; 
5865            _versionMadeByPlatform  =  ( ZipVersionMadeByPlatform ) cd . VersionMadeByCompatibility ; 
@@ -84,12 +91,11 @@ internal ZipArchiveEntry(ZipArchive archive, ZipCentralDirectoryFileHeader cd)
8491            _lhUnknownExtraFields  =  null ; 
8592            // the cd should have this as null if we aren't in Update mode 
8693            _cdUnknownExtraFields  =  cd . ExtraFields ; 
94+             _cdTrailingExtraFieldData  =  cd . TrailingExtraFieldData ; 
8795
8896            _fileComment  =  cd . FileComment ; 
8997
9098            _compressionLevel  =  MapCompressionLevel ( _generalPurposeBitFlag ,  CompressionMethod ) ; 
91- 
92-             Changes  =  ZipArchive . ChangeState . Unchanged ; 
9399        } 
94100
95101        // Initializes a ZipArchiveEntry instance for a new archive entry with a specified compression level. 
@@ -543,8 +549,9 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite)
543549
544550
545551            // determine if we can fit zip64 extra field and original extra fields all in 
552+             int  currExtraFieldDataLength  =  ZipGenericExtraField . TotalSize ( _cdUnknownExtraFields ,  _cdTrailingExtraFieldData ? . Length  ??  0 ) ; 
546553            int  bigExtraFieldLength  =  ( zip64ExtraField  !=  null  ?  zip64ExtraField . TotalSize  :  0 ) 
547-                                       +  ( _cdUnknownExtraFields   !=   null   ?   ZipGenericExtraField . TotalSize ( _cdUnknownExtraFields )   :   0 ) ; 
554+                                       +  currExtraFieldDataLength ; 
548555            ushort  extraFieldLength ; 
549556            if  ( bigExtraFieldLength  >  ushort . MaxValue ) 
550557            { 
@@ -561,7 +568,7 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite)
561568                long  centralDirectoryHeaderLength  =  ZipCentralDirectoryFileHeader . FieldLocations . DynamicData 
562569                    +  _storedEntryNameBytes . Length 
563570                    +  ( zip64ExtraField  !=  null  ?  zip64ExtraField . TotalSize  :  0 ) 
564-                     +  ( _cdUnknownExtraFields   !=   null   ?   ZipGenericExtraField . TotalSize ( _cdUnknownExtraFields )   :   0 ) 
571+                     +  currExtraFieldDataLength 
565572                    +  _fileComment . Length ; 
566573
567574                _archive . ArchiveStream . Seek ( centralDirectoryHeaderLength ,  SeekOrigin . Current ) ; 
@@ -609,13 +616,11 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite)
609616                _archive . ArchiveStream . Write ( cdStaticHeader ) ; 
610617                _archive . ArchiveStream . Write ( _storedEntryNameBytes ) ; 
611618
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) 
613620                zip64ExtraField ? . WriteBlock ( _archive . ArchiveStream ) ; 
614621
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 ) ; 
619624
620625                if  ( _fileComment . Length  >  0 ) 
621626                { 
@@ -626,7 +631,7 @@ internal void WriteCentralDirectoryFileHeader(bool forceWrite)
626631
627632        // throws exception if fails, will get called on every relevant entry before closing in update mode 
628633        // can throw InvalidDataException 
629-         internal  void  LoadLocalHeaderExtraFieldAndCompressedBytesIfNeeded ( ) 
634+         internal  void  LoadLocalHeaderExtraFieldIfNeeded ( ) 
630635        { 
631636            // we should have made this exact call in _archive.Init through ThrowIfOpenable 
632637            Debug . Assert ( IsOpenable ( false ,  true ,  out  _ ) ) ; 
@@ -635,8 +640,16 @@ internal void LoadLocalHeaderExtraFieldAndCompressedBytesIfNeeded()
635640            if  ( _originallyInArchive ) 
636641            { 
637642                _archive . ArchiveStream . Seek ( _offsetOfLocalHeader ,  SeekOrigin . Begin ) ; 
638-                 _lhUnknownExtraFields  =  ZipLocalFileHeader . GetExtraFields ( _archive . ArchiveStream ) ; 
643+                 _lhUnknownExtraFields  =  ZipLocalFileHeader . GetExtraFields ( _archive . ArchiveStream ,   out   _lhTrailingExtraFieldData ) ; 
639644            } 
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  _ ) ) ; 
640653
641654            if  ( ! _everOpenedForWrite  &&  _originallyInArchive ) 
642655            { 
@@ -979,8 +992,9 @@ private bool WriteLocalFileHeader(bool isEmptyFile, bool forceWrite)
979992            _offsetOfLocalHeader  =  _archive . ArchiveStream . Position ; 
980993
981994            // 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 ) ; 
982996            int  bigExtraFieldLength  =  ( zip64ExtraField  !=  null  ?  zip64ExtraField . TotalSize  :  0 ) 
983-                                       +  ( _lhUnknownExtraFields   !=   null   ?   ZipGenericExtraField . TotalSize ( _lhUnknownExtraFields )   :   0 ) ; 
997+                                       +  currExtraFieldDataLength ; 
984998            ushort  extraFieldLength ; 
985999            if  ( bigExtraFieldLength  >  ushort . MaxValue ) 
9861000            { 
@@ -1003,10 +1017,7 @@ private bool WriteLocalFileHeader(bool isEmptyFile, bool forceWrite)
10031017                    _archive . ArchiveStream . Seek ( zip64ExtraField . TotalSize ,  SeekOrigin . Current ) ; 
10041018                } 
10051019
1006-                 if  ( _lhUnknownExtraFields  !=  null ) 
1007-                 { 
1008-                     _archive . ArchiveStream . Seek ( ZipGenericExtraField . TotalSize ( _lhUnknownExtraFields ) ,  SeekOrigin . Current ) ; 
1009-                 } 
1020+                 _archive . ArchiveStream . Seek ( currExtraFieldDataLength ,  SeekOrigin . Current ) ; 
10101021            } 
10111022            else 
10121023            { 
@@ -1029,8 +1040,7 @@ private bool WriteLocalFileHeader(bool isEmptyFile, bool forceWrite)
10291040                // Only when handling zip64 
10301041                zip64ExtraField ? . WriteBlock ( _archive . ArchiveStream ) ; 
10311042
1032-                 if  ( _lhUnknownExtraFields  !=  null ) 
1033-                     ZipGenericExtraField . WriteAllBlocks ( _lhUnknownExtraFields ,  _archive . ArchiveStream ) ; 
1043+                 ZipGenericExtraField . WriteAllBlocks ( _lhUnknownExtraFields ,  _lhTrailingExtraFieldData  ??  Array . Empty < byte > ( ) ,  _archive . ArchiveStream ) ; 
10341044            } 
10351045
10361046            return  zip64ExtraField  !=  null ; 
@@ -1252,10 +1262,12 @@ private void VersionToExtractAtLeast(ZipVersionNeededValues value)
12521262            if  ( _versionToExtract  <  value ) 
12531263            { 
12541264                _versionToExtract  =  value ; 
1265+                 Changes  |=  ZipArchive . ChangeState . FixedLengthMetadata ; 
12551266            } 
12561267            if  ( _versionMadeBySpecification  <  value ) 
12571268            { 
12581269                _versionMadeBySpecification  =  value ; 
1270+                 Changes  |=  ZipArchive . ChangeState . FixedLengthMetadata ; 
12591271            } 
12601272        } 
12611273
0 commit comments