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 4c9340bdaf853c111ca1d5aaeeea5d861d21d4bf Author: Michael Stahl <[email protected]> AuthorDate: Fri Sep 19 20:06:14 2025 +0200 Commit: Noel Grandin <[email protected]> CommitDate: Wed Oct 1 20:02:15 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]> (cherry picked from commit abfdc11fa45610f38d00999f66416c66a868426a) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191736 Tested-by: Jenkins CollaboraOffice <[email protected]> diff --git a/package/inc/ZipOutputStream.hxx b/package/inc/ZipOutputStream.hxx index 5b04a0e41dc8..8ffc623e564b 100644 --- a/package/inc/ZipOutputStream.hxx +++ b/package/inc/ZipOutputStream.hxx @@ -80,7 +80,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 18fd18a2c6a5..5fd8cc3770c0 100644 --- a/package/source/zippackage/ZipPackage.cxx +++ b/package/source/zippackage/ZipPackage.cxx @@ -1178,13 +1178,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); @@ -1204,7 +1205,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. @@ -1245,6 +1246,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 8280a048c7c5..85f18f47709a 100644 --- a/package/source/zippackage/ZipPackageStream.cxx +++ b/package/source/zippackage/ZipPackageStream.cxx @@ -511,7 +511,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(); @@ -541,6 +549,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 @@ -742,7 +755,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);
