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 <noel.grandin@collabora.co.uk>
Reviewed-by: Michael Stahl <michael.stahl@collabora.com>
This commit is contained in:
Michael Stahl
2025-09-23 11:42:58 +02:00
parent 66e783a224
commit abfdc11fa4
4 changed files with 36 additions and 13 deletions
+1 -1
View File
@@ -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);
+16 -8
View File
@@ -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));
}
}
+4 -2
View File
@@ -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);
+15 -2
View File
@@ -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);