@@ -1062,7 +1062,7 @@ private static BitFlagValues MapDeflateCompressionOption(BitFlagValues generalPu
10621062
10631063 private bool ShouldUseZIP64 => AreSizesTooLarge || IsOffsetTooLarge ;
10641064
1065- private bool WriteLocalFileHeaderInitialize ( bool isEmptyFile , bool forceWrite , out Zip64ExtraField ? zip64ExtraField , out uint compressedSizeTruncated , out uint uncompressedSizeTruncated , out ushort extraFieldLength )
1065+ private bool WriteLocalFileHeaderInitialize ( bool isEmptyFile , bool forceWrite , bool preserveDataDescriptor , out Zip64ExtraField ? zip64ExtraField , out uint compressedSizeTruncated , out uint uncompressedSizeTruncated , out ushort extraFieldLength , out uint crc32ToWrite )
10661066 {
10671067 // _entryname only gets set when we read in or call moveTo. MoveTo does a check, and
10681068 // reading in should not be able to produce an entryname longer than ushort.MaxValue
@@ -1080,6 +1080,7 @@ private bool WriteLocalFileHeaderInitialize(bool isEmptyFile, bool forceWrite, o
10801080 CompressionMethod = ZipCompressionMethod . Stored ;
10811081 compressedSizeTruncated = 0 ;
10821082 uncompressedSizeTruncated = 0 ;
1083+ crc32ToWrite = 0 ;
10831084 Debug . Assert ( _uncompressedSize == 0 ) ;
10841085 Debug . Assert ( _crc32 == 0 ) ;
10851086 }
@@ -1092,11 +1093,22 @@ private bool WriteLocalFileHeaderInitialize(bool isEmptyFile, bool forceWrite, o
10921093 _generalPurposeBitFlag |= BitFlagValues . DataDescriptor ;
10931094 compressedSizeTruncated = 0 ;
10941095 uncompressedSizeTruncated = 0 ;
1096+ crc32ToWrite = 0 ;
10951097 // the crc should not have been set if we are in create mode, but clear it just to be sure
10961098 Debug . Assert ( _crc32 == 0 ) ;
10971099 }
1100+ else if ( preserveDataDescriptor )
1101+ {
1102+ // The existing data descriptor bytes will remain on disk. Preserve bit 3 and
1103+ // write zeros for CRC and sizes, matching the original streaming format, so that
1104+ // sequential readers are not confused by the on-disk descriptor record.
1105+ compressedSizeTruncated = 0 ;
1106+ uncompressedSizeTruncated = 0 ;
1107+ crc32ToWrite = 0 ;
1108+ }
10981109 else // if we are not in streaming mode, we have to decide if we want to write zip64 headers
10991110 {
1111+ crc32ToWrite = _crc32 ;
11001112 if ( ShouldUseZIP64
11011113#if DEBUG_FORCE_ZIP64
11021114 || ( _archive . _forceZip64 && _archive . Mode == ZipArchiveMode . Update )
@@ -1157,37 +1169,38 @@ private bool WriteLocalFileHeaderInitialize(bool isEmptyFile, bool forceWrite, o
11571169 return false ;
11581170 }
11591171
1160- // We are writing the header. For seekable/empty-file paths the sizes are written
1161- // directly into the header, so a data descriptor is not needed.
1162- if ( isEmptyFile || _archive . ArchiveStream . CanSeek )
1172+ // We are writing the header. Clear the data descriptor bit unless the existing
1173+ // on-disk data descriptor is being preserved, in which case bit 3 must remain set
1174+ // so sequential readers know to expect the descriptor record after the compressed data.
1175+ if ( ! preserveDataDescriptor && ( isEmptyFile || _archive . ArchiveStream . CanSeek ) )
11631176 {
11641177 _generalPurposeBitFlag &= ~ BitFlagValues . DataDescriptor ;
11651178 }
11661179
11671180 return true ;
11681181 }
11691182
1170- private void WriteLocalFileHeaderPrepare ( Span < byte > lfStaticHeader , uint compressedSizeTruncated , uint uncompressedSizeTruncated , ushort extraFieldLength )
1183+ private void WriteLocalFileHeaderPrepare ( Span < byte > lfStaticHeader , uint compressedSizeTruncated , uint uncompressedSizeTruncated , ushort extraFieldLength , uint crc32ToWrite )
11711184 {
11721185 ZipLocalFileHeader . SignatureConstantBytes . CopyTo ( lfStaticHeader [ ZipLocalFileHeader . FieldLocations . Signature ..] ) ;
11731186 BinaryPrimitives . WriteUInt16LittleEndian ( lfStaticHeader [ ZipLocalFileHeader . FieldLocations . VersionNeededToExtract ..] , ( ushort ) _versionToExtract ) ;
11741187 BinaryPrimitives . WriteUInt16LittleEndian ( lfStaticHeader [ ZipLocalFileHeader . FieldLocations . GeneralPurposeBitFlags ..] , ( ushort ) _generalPurposeBitFlag ) ;
11751188 BinaryPrimitives . WriteUInt16LittleEndian ( lfStaticHeader [ ZipLocalFileHeader . FieldLocations . CompressionMethod ..] , ( ushort ) CompressionMethod ) ;
11761189 BinaryPrimitives . WriteUInt32LittleEndian ( lfStaticHeader [ ZipLocalFileHeader . FieldLocations . LastModified ..] , ZipHelper . DateTimeToDosTime ( _lastModified . DateTime ) ) ;
1177- BinaryPrimitives . WriteUInt32LittleEndian ( lfStaticHeader [ ZipLocalFileHeader . FieldLocations . Crc32 ..] , _crc32 ) ;
1190+ BinaryPrimitives . WriteUInt32LittleEndian ( lfStaticHeader [ ZipLocalFileHeader . FieldLocations . Crc32 ..] , crc32ToWrite ) ;
11781191 BinaryPrimitives . WriteUInt32LittleEndian ( lfStaticHeader [ ZipLocalFileHeader . FieldLocations . CompressedSize ..] , compressedSizeTruncated ) ;
11791192 BinaryPrimitives . WriteUInt32LittleEndian ( lfStaticHeader [ ZipLocalFileHeader . FieldLocations . UncompressedSize ..] , uncompressedSizeTruncated ) ;
11801193 BinaryPrimitives . WriteUInt16LittleEndian ( lfStaticHeader [ ZipLocalFileHeader . FieldLocations . FilenameLength ..] , ( ushort ) _storedEntryNameBytes . Length ) ;
11811194 BinaryPrimitives . WriteUInt16LittleEndian ( lfStaticHeader [ ZipLocalFileHeader . FieldLocations . ExtraFieldLength ..] , extraFieldLength ) ;
11821195 }
11831196
11841197 // return value is true if we allocated an extra field for 64 bit headers, un/compressed size
1185- private bool WriteLocalFileHeader ( bool isEmptyFile , bool forceWrite )
1198+ private bool WriteLocalFileHeader ( bool isEmptyFile , bool forceWrite , bool preserveDataDescriptor = false )
11861199 {
1187- if ( WriteLocalFileHeaderInitialize ( isEmptyFile , forceWrite , out Zip64ExtraField ? zip64ExtraField , out uint compressedSizeTruncated , out uint uncompressedSizeTruncated , out ushort extraFieldLength ) )
1200+ if ( WriteLocalFileHeaderInitialize ( isEmptyFile , forceWrite , preserveDataDescriptor , out Zip64ExtraField ? zip64ExtraField , out uint compressedSizeTruncated , out uint uncompressedSizeTruncated , out ushort extraFieldLength , out uint crc32ToWrite ) )
11881201 {
11891202 Span < byte > lfStaticHeader = stackalloc byte [ ZipLocalFileHeader . SizeOfLocalHeader ] ;
1190- WriteLocalFileHeaderPrepare ( lfStaticHeader , compressedSizeTruncated , uncompressedSizeTruncated , extraFieldLength ) ;
1203+ WriteLocalFileHeaderPrepare ( lfStaticHeader , compressedSizeTruncated , uncompressedSizeTruncated , extraFieldLength , crc32ToWrite ) ;
11911204
11921205 // write header
11931206 _archive . ArchiveStream . Write ( lfStaticHeader ) ;
@@ -1261,7 +1274,11 @@ private void WriteLocalFileHeaderAndDataIfNeeded(bool forceWrite)
12611274 if ( _archive . Mode == ZipArchiveMode . Update || ! _everOpenedForWrite )
12621275 {
12631276 _everOpenedForWrite = true ;
1264- WriteLocalFileHeader ( isEmptyFile : _uncompressedSize == 0 , forceWrite : forceWrite ) ;
1277+ // If the entry originally used a data descriptor and we are not rewriting the data,
1278+ // the descriptor record remains on disk after the compressed bytes. Preserve bit 3
1279+ // and write zeros for CRC/sizes so that sequential readers see a consistent entry.
1280+ bool preserveDataDescriptor = _originallyInArchive && ( _generalPurposeBitFlag & BitFlagValues . DataDescriptor ) != 0 ;
1281+ WriteLocalFileHeader ( isEmptyFile : _uncompressedSize == 0 , forceWrite : forceWrite , preserveDataDescriptor : preserveDataDescriptor ) ;
12651282
12661283 // Advance the stream past the compressed data and any trailing data descriptor
12671284 // by seeking to the pre-computed end-of-entry boundary.
0 commit comments