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:
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user