package/inc/ZipOutputStream.hxx | 2 +- package/source/zipapi/ZipOutputStream.cxx | 24 ++++++++++++++++-------- package/source/zippackage/ZipPackage.cxx | 6 ++++-- package/source/zippackage/ZipPackageStream.cxx | 17 +++++++++++++++-- 4 files changed, 36 insertions(+), 13 deletions(-)
New commits: commit abfdc11fa45610f38d00999f66416c66a868426a Author: Michael Stahl <[email protected]> AuthorDate: Fri Sep 19 20:06:14 2025 +0200 Commit: Michael Stahl <[email protected]> CommitDate: Tue Sep 23 11:42:58 2025 +0200 tdf#167205 package: fix writing Zip64 local headers This would be reported on reading as an 8-byte gap at the position where a 32-bit DD for the entry would end. The problem is that if flag bit 3 is set, and the uncompressed size exceeds 2^32, writeDataDescriptor() will write 8-byte sizes, but the Zip64 extension header is missing - the latter should also write 0 even though the APPNOTE doens't appear to require it. 4.3.9.2 [...] When extracting, if the zip64 extended information extra field is present for the file the compressed and uncompressed sizes will be 8 byte values. Also make sure that the ZipEntry nSize member actually contains the uncompressed size, which is somewhat tricky, but is required in writeLOC() to check if Zip64 is needed. (Assume that the DEFLATEd size is never larger than uncompressed, which is true for every XML file). Change-Id: I611e6ffda12d8e6953e9306dc4a4407be8e725a0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191185 Tested-by: Jenkins Reviewed-by: Noel Grandin <[email protected]> Reviewed-by: Michael Stahl <[email protected]> diff --git a/package/inc/ZipOutputStream.hxx b/package/inc/ZipOutputStream.hxx index 45f78b729250..5c7671dbf37a 100644 --- a/package/inc/ZipOutputStream.hxx +++ b/package/inc/ZipOutputStream.hxx @@ -78,7 +78,7 @@ private: /// @throws css::io::IOException /// @throws css::uno::RuntimeException void writeDataDescriptor( const ZipEntry &rEntry ); - void writeExtraFields( const ZipEntry& rEntry ); + void writeExtraFields(const ZipEntry& rEntry, bool isLOCWithDD); // ScheduledThread handling helpers void consumeScheduledThreadTaskEntry(std::unique_ptr<ZipOutputEntryInThread> pCandidate); diff --git a/package/source/zipapi/ZipOutputStream.cxx b/package/source/zipapi/ZipOutputStream.cxx index da265c303b13..2217ce518aa8 100644 --- a/package/source/zipapi/ZipOutputStream.cxx +++ b/package/source/zipapi/ZipOutputStream.cxx @@ -64,7 +64,12 @@ void ZipOutputStream::setEntry(ZipEntry& rEntry) if (rEntry.nSize == -1 || rEntry.nCompressedSize == -1 || rEntry.nCrc == -1) { - rEntry.nSize = rEntry.nCompressedSize = 0; + if (rEntry.nSize == -1) + { + assert(false); // how to get here + rEntry.nSize = 0; + } + rEntry.nCompressedSize = 0; rEntry.nFlag |= 8; } } @@ -251,7 +256,7 @@ void ZipOutputStream::writeCEN( const ZipEntry &rEntry ) if (bWrite64Header) { - writeExtraFields( rEntry ); + writeExtraFields(rEntry, false); } } @@ -276,13 +281,13 @@ void ZipOutputStream::writeDataDescriptor(const ZipEntry& rEntry) } } -void ZipOutputStream::writeExtraFields(const ZipEntry& rEntry) +void ZipOutputStream::writeExtraFields(const ZipEntry& rEntry, bool const isLOCWithDD) { //Could contain more fields, now we only save Zip64 extended information m_aChucker.WriteInt16( 1 ); //id of Zip64 extended information extra field m_aChucker.WriteInt16( 28 ); //data size of this field = 3*8+4 byte - m_aChucker.WriteUInt64( rEntry.nSize ); - m_aChucker.WriteUInt64( rEntry.nCompressedSize ); + m_aChucker.WriteUInt64(isLOCWithDD ? 0 : rEntry.nSize); + m_aChucker.WriteUInt64(isLOCWithDD ? 0 : rEntry.nCompressedSize); m_aChucker.WriteUInt64( rEntry.nOffset ); m_aChucker.WriteInt32( 0 ); //Number of the disk on which this file starts } @@ -311,6 +316,9 @@ void ZipOutputStream::writeLOC(std::unique_ptr<ZipEntry>&& pEntry, bool bEncrypt m_aChucker.WriteInt16( rEntry.nMethod ); bool bWrite64Header = false; + // getTruncated must always be called to init bWrite64Header! + auto const nTruncCompressedSize{getTruncated(rEntry.nCompressedSize, &bWrite64Header)}; + auto const nTruncSize{getTruncated(rEntry.nSize, &bWrite64Header)}; m_aChucker.WriteUInt32( rEntry.nTime ); if ((rEntry.nFlag & 8) == 8 ) @@ -322,8 +330,8 @@ void ZipOutputStream::writeLOC(std::unique_ptr<ZipEntry>&& pEntry, bool bEncrypt else { m_aChucker.WriteUInt32( rEntry.nCrc ); - m_aChucker.WriteUInt32( getTruncated( rEntry.nCompressedSize, &bWrite64Header ) ); - m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) ); + m_aChucker.WriteUInt32(nTruncCompressedSize); + m_aChucker.WriteUInt32(nTruncSize); } m_aChucker.WriteInt16( nNameLength ); m_aChucker.WriteInt16( bWrite64Header ? 32 : 0 ); @@ -335,7 +343,7 @@ void ZipOutputStream::writeLOC(std::unique_ptr<ZipEntry>&& pEntry, bool bEncrypt if (bWrite64Header) { - writeExtraFields(rEntry); + writeExtraFields(rEntry, (rEntry.nFlag & 8)); } } diff --git a/package/source/zippackage/ZipPackage.cxx b/package/source/zippackage/ZipPackage.cxx index 2364ce12d6ec..2f420170c70f 100644 --- a/package/source/zippackage/ZipPackage.cxx +++ b/package/source/zippackage/ZipPackage.cxx @@ -1180,13 +1180,14 @@ void ZipPackage::WriteManifest( ZipOutputStream& aZipOut, const std::vector< uno pEntry->sPath = "META-INF/manifest.xml"; pEntry->nMethod = DEFLATED; pEntry->nCrc = -1; - pEntry->nSize = pEntry->nCompressedSize = -1; + pEntry->nCompressedSize = -1; pEntry->nTime = ZipOutputStream::getCurrentDosTime(); xWriter->writeManifestSequence ( pBuffer, comphelper::containerToSequence(aManList) ); sal_Int32 nBufferLength = static_cast < sal_Int32 > ( pBuffer->getPosition() ); pBuffer->realloc( nBufferLength ); + pEntry->nSize = nBufferLength; // the manifest.xml is never encrypted - so pass an empty reference ZipOutputStream::setEntry(*pEntry); @@ -1206,7 +1207,7 @@ void ZipPackage::WriteContentTypes( ZipOutputStream& aZipOut, const std::vector< pEntry->sPath = "[Content_Types].xml"; pEntry->nMethod = DEFLATED; pEntry->nCrc = -1; - pEntry->nSize = pEntry->nCompressedSize = -1; + pEntry->nCompressedSize = -1; pEntry->nTime = ZipOutputStream::getCurrentDosTime(); // Add default entries, the count must be updated manually when appending. @@ -1247,6 +1248,7 @@ void ZipPackage::WriteContentTypes( ZipOutputStream& aZipOut, const std::vector< sal_Int32 nBufferLength = static_cast < sal_Int32 > ( pBuffer->getPosition() ); pBuffer->realloc( nBufferLength ); + pEntry->nSize = nBufferLength; // there is no encryption in this format currently ZipOutputStream::setEntry(*pEntry); diff --git a/package/source/zippackage/ZipPackageStream.cxx b/package/source/zippackage/ZipPackageStream.cxx index 32ef48b954e0..78366a82a6fa 100644 --- a/package/source/zippackage/ZipPackageStream.cxx +++ b/package/source/zippackage/ZipPackageStream.cxx @@ -510,7 +510,15 @@ bool ZipPackageStream::saveChild( bUseNonSeekableAccess = ( xStream.is() && !xSeek.is() ); } - if ( !bUseNonSeekableAccess ) + if (bUseNonSeekableAccess) + { + // this should work for XUnbufferedStream/OInputCompStream at least + if (pTempEntry->nSize == -1) + { // this is needed in writeLOC to detect Zip64 + pTempEntry->nSize = xStream->available(); + } + } + else { xStream = getRawData(); @@ -540,6 +548,11 @@ bool ZipPackageStream::saveChild( m_nOwnStreamOrigSize = xSeek->getLength(); } + if (pTempEntry->nSize == -1) + { // this is needed in writeLOC to detect Zip64 + pTempEntry->nSize = xSeek->getLength(); + } + xSeek->seek ( 0 ); } else @@ -741,7 +754,7 @@ bool ZipPackageStream::saveChild( { pTempEntry->nMethod = DEFLATED; pTempEntry->nCrc = -1; - pTempEntry->nCompressedSize = pTempEntry->nSize = -1; + pTempEntry->nCompressedSize = -1; } uno::Reference< io::XSeekable > xSeek(xStream, uno::UNO_QUERY);
