tdf#82984 tdf#94915 zip64 support (import + export)
Implemented import + export for "Zip64 Extended Information Extra Field",
(in "Local file header" and "Central directory file header")
and for Data descriptor.
Focused only to be able to handle files with over 4GB uncompressed size,
in the zip archive.
The 64k filecount, and the 4GB compressed size limit is probably still present
Tried to follow pkware .ZIP File Format Specification,
Some cases were not clear to me and/or some zip compressing tool may not
perfectly follow the standard, like 'extra field' should be 28 bytes long,
but its reader now can read shorter (or longer) 'extra field'.
Replaced some 32bit codes with 64bit codes, in stream handling, in deflater.
Tested with an ods file that contained a content.xml that bigger then 4BG+
(import + export + reimport) on windows.
I think 4GB+ files import/export would be too slow fot unittest.
So, for unit test, used the small but zip64 format files,
that was attached to the bugzilla tickets
Note: It helps with Bug 128244 too (1 of the unittest tests it),
but that ods file missing manifest.xml, so LO won't be able to import it.
Change-Id: Idfeb90594388fd34ae719677f5d268ca9a484fb1
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/147306
Tested-by: Jenkins
Reviewed-by: Michael Meeks <michael.meeks@collabora.com>
diff --git a/include/package/Deflater.hxx b/include/package/Deflater.hxx
index 3cd528b..2a5c9d1 100644
--- a/include/package/Deflater.hxx
+++ b/include/package/Deflater.hxx
@@ -36,6 +36,8 @@ class DLLPUBLIC_PACKAGE Deflater final
bool bFinish;
bool bFinished;
sal_Int64 nOffset, nLength;
// zlib total_in / total_out may be stored in 32bit, so they can overflow in case of 4gb files
sal_uInt64 nTotalOut64, nTotalIn64; // save the overflowed value here.
std::unique_ptr<z_stream> pStream;
void init (sal_Int32 nLevel, bool bNowrap);
diff --git a/package/inc/ByteChucker.hxx b/package/inc/ByteChucker.hxx
index 707b678..c502ad6 100644
--- a/package/inc/ByteChucker.hxx
+++ b/package/inc/ByteChucker.hxx
@@ -29,8 +29,8 @@ class ByteChucker final
{
css::uno::Reference < css::io::XOutputStream > xStream;
css::uno::Reference < css::io::XSeekable > xSeek;
css::uno::Sequence < sal_Int8 > a2Sequence, a4Sequence;
sal_Int8 * const p2Sequence, * const p4Sequence;
css::uno::Sequence < sal_Int8 > a2Sequence, a4Sequence, a8Sequence;
sal_Int8 * const p2Sequence, * const p4Sequence, * const p8Sequence;
public:
ByteChucker (css::uno::Reference<css::io::XOutputStream> const & xOstream);
@@ -70,6 +70,32 @@ public:
p4Sequence[3] = static_cast < sal_Int8 > ((nuInt32 >> 24 ) & 0xFF);
WriteBytes( a4Sequence );
}
void WriteInt64(sal_Int64 nInt64)
{
p8Sequence[0] = static_cast<sal_Int8>((nInt64 >> 0) & 0xFF);
p8Sequence[1] = static_cast<sal_Int8>((nInt64 >> 8) & 0xFF);
p8Sequence[2] = static_cast<sal_Int8>((nInt64 >> 16) & 0xFF);
p8Sequence[3] = static_cast<sal_Int8>((nInt64 >> 24) & 0xFF);
p8Sequence[4] = static_cast<sal_Int8>((nInt64 >> 32) & 0xFF);
p8Sequence[5] = static_cast<sal_Int8>((nInt64 >> 40) & 0xFF);
p8Sequence[6] = static_cast<sal_Int8>((nInt64 >> 48) & 0xFF);
p8Sequence[7] = static_cast<sal_Int8>((nInt64 >> 56) & 0xFF);
WriteBytes( a8Sequence );
}
void WriteUInt64(sal_uInt64 nuInt64)
{
p8Sequence[0] = static_cast<sal_Int8>((nuInt64 >> 0) & 0xFF);
p8Sequence[1] = static_cast<sal_Int8>((nuInt64 >> 8) & 0xFF);
p8Sequence[2] = static_cast<sal_Int8>((nuInt64 >> 16) & 0xFF);
p8Sequence[3] = static_cast<sal_Int8>((nuInt64 >> 24) & 0xFF);
p8Sequence[4] = static_cast<sal_Int8>((nuInt64 >> 32) & 0xFF);
p8Sequence[5] = static_cast<sal_Int8>((nuInt64 >> 40) & 0xFF);
p8Sequence[6] = static_cast<sal_Int8>((nuInt64 >> 48) & 0xFF);
p8Sequence[7] = static_cast<sal_Int8>((nuInt64 >> 56) & 0xFF);
WriteBytes( a8Sequence );
}
};
#endif
diff --git a/package/inc/ZipFile.hxx b/package/inc/ZipFile.hxx
index 7fe15f7..f6b1849 100644
--- a/package/inc/ZipFile.hxx
+++ b/package/inc/ZipFile.hxx
@@ -29,6 +29,7 @@
#include "HashMaps.hxx"
#include "EncryptionData.hxx"
class MemoryByteGrabber;
namespace com::sun::star {
namespace uno { class XComponentContext; }
namespace ucb { class XProgressHandler; }
@@ -81,6 +82,9 @@ class ZipFile
sal_Int32 readCEN();
sal_Int32 findEND();
void recover();
static void readExtraFields(MemoryByteGrabber& aMemGrabber, sal_Int16 nExtraLen,
sal_uInt64& nSize, sal_uInt64& nCompressedSize,
sal_uInt64* nOffset);
public:
diff --git a/package/inc/ZipOutputStream.hxx b/package/inc/ZipOutputStream.hxx
index f55ef59..b31af9d 100644
--- a/package/inc/ZipOutputStream.hxx
+++ b/package/inc/ZipOutputStream.hxx
@@ -78,7 +78,8 @@ private:
void writeCEN( const ZipEntry &rEntry );
/// @throws css::io::IOException
/// @throws css::uno::RuntimeException
void writeEXT( const ZipEntry &rEntry );
void writeDataDescriptor( const ZipEntry &rEntry );
void writeExtraFields( const ZipEntry& rEntry );
// ScheduledThread handling helpers
void consumeScheduledThreadTaskEntry(std::unique_ptr<ZipOutputEntryInThread> pCandidate);
diff --git a/package/qa/cppunit/data/export64.zip b/package/qa/cppunit/data/export64.zip
new file mode 100644
index 0000000..b303266
--- /dev/null
+++ b/package/qa/cppunit/data/export64.zip
Binary files differ
diff --git a/package/qa/cppunit/test_package.cxx b/package/qa/cppunit/test_package.cxx
index 022d8a9..911e0ea 100644
--- a/package/qa/cppunit/test_package.cxx
+++ b/package/qa/cppunit/test_package.cxx
@@ -36,11 +36,13 @@ namespace
void test();
void testThreadedStreams();
void testBufferedThreadedStreams();
void testZip64();
CPPUNIT_TEST_SUITE(PackageTest);
CPPUNIT_TEST(test);
CPPUNIT_TEST(testThreadedStreams);
CPPUNIT_TEST(testBufferedThreadedStreams);
CPPUNIT_TEST(testZip64);
CPPUNIT_TEST_SUITE_END();
private:
@@ -198,6 +200,53 @@ namespace
verifyStreams( aTestBuffers );
}
void PackageTest::testZip64()
{
// This small zip file have 2 files (content.xml, styles.xml) that have
// Zip64 Extended Information Extra Field in both
// "Local file header" and "Central directory file header",
// and have ZIP64 format "Data descriptor".
OUString aURL2 = m_directories.getURLFromSrc(u"/package/qa/cppunit/data/export64.zip");
uno::Sequence<beans::NamedValue> aNVs2{ { "URL", uno::Any(aURL2) } };
uno::Sequence<uno::Any> aArgs2{ uno::Any(aNVs2) };
uno::Reference<uno::XComponentContext> xCxt = comphelper::getProcessComponentContext();
uno::Reference<lang::XMultiComponentFactory> xSvcMgr = xCxt->getServiceManager();
// Without Zip64 support, it would crash here
uno::Reference<packages::zip::XZipFileAccess2> xZip2(
xSvcMgr->createInstanceWithArgumentsAndContext(
"com.sun.star.packages.zip.ZipFileAccess", aArgs2, xCxt),
uno::UNO_QUERY);
CPPUNIT_ASSERT(xZip2.is());
uno::Reference<container::XNameAccess> xNA;
xNA = xZip2;
CPPUNIT_ASSERT(xNA.is());
// Check if the styles.xml seems to be right
uno::Reference<io::XInputStream> xStrm;
xNA->getByName("styles.xml") >>= xStrm;
CPPUNIT_ASSERT(xStrm.is());
// Filesize check
sal_Int32 nSize = xStrm->available();
CPPUNIT_ASSERT_EQUAL(sal_Int32(1112), nSize);
uno::Sequence<sal_Int8> aBytes;
sal_Int32 nBytesRead = xStrm->readBytes(aBytes, nSize);
const sal_Int8* p = aBytes.getArray();
CPPUNIT_ASSERT_EQUAL(sal_Int32(1112), nBytesRead);
// Check the uncompressed styles.xml file content.
OString aFile(static_cast<const char*>(static_cast<const void*>(p)), nSize);
CPPUNIT_ASSERT(aFile.startsWith(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<office:document-styles"));
CPPUNIT_ASSERT(aFile.endsWith(
"</number:time-style>\r\n </office:styles>\r\n</office:document-styles>\r\n"));
}
CPPUNIT_TEST_SUITE_REGISTRATION(PackageTest);
}
diff --git a/package/source/zipapi/ByteChucker.cxx b/package/source/zipapi/ByteChucker.cxx
index 6c5994a..fe1f6a8 100644
--- a/package/source/zipapi/ByteChucker.cxx
+++ b/package/source/zipapi/ByteChucker.cxx
@@ -29,8 +29,10 @@ ByteChucker::ByteChucker(Reference<XOutputStream> const & xOstream)
, xSeek (xOstream, UNO_QUERY )
, a2Sequence ( 2 )
, a4Sequence ( 4 )
, a8Sequence ( 8 )
, p2Sequence ( a2Sequence.getArray() )
, p4Sequence ( a4Sequence.getArray() )
, p8Sequence ( a8Sequence.getArray() )
{
}
diff --git a/package/source/zipapi/Deflater.cxx b/package/source/zipapi/Deflater.cxx
index 5c0fd70..2c66253 100644
--- a/package/source/zipapi/Deflater.cxx
+++ b/package/source/zipapi/Deflater.cxx
@@ -62,6 +62,8 @@ Deflater::Deflater(sal_Int32 nSetLevel, bool bNowrap)
, bFinished(false)
, nOffset(0)
, nLength(0)
, nTotalOut64(0)
, nTotalIn64(0)
{
init(nSetLevel, bNowrap);
}
@@ -73,12 +75,24 @@ sal_Int32 Deflater::doDeflateBytes (uno::Sequence < sal_Int8 > &rBuffer, sal_Int
pStream->next_out = reinterpret_cast<unsigned char*>(rBuffer.getArray())+nNewOffset;
pStream->avail_in = nLength;
pStream->avail_out = nNewLength;
auto nLastTotalIn = pStream->total_in;
auto nLastTotalOut = pStream->total_out;
#if !defined Z_PREFIX
nResult = deflate(pStream.get(), bFinish ? Z_FINISH : Z_NO_FLUSH);
#else
nResult = z_deflate(pStream.get(), bFinish ? Z_FINISH : Z_NO_FLUSH);
#endif
// total_in / total_out may stored only in 32bit, and can owerflow during deflate
// 1 deflate call, uncompress only a few data, so only 1 overflow can happen at once.
if (pStream->total_in < nLastTotalIn)
{
nTotalIn64 += 0x100000000;
}
if (pStream->total_out < nLastTotalOut)
{
nTotalOut64 += 0x100000000;
}
switch (nResult)
{
case Z_STREAM_END:
@@ -115,11 +129,11 @@ sal_Int32 Deflater::doDeflateSegment( uno::Sequence< sal_Int8 >& rBuffer, sal_In
}
sal_Int64 Deflater::getTotalIn() const
{
return pStream->total_in; // FIXME64: zlib doesn't look 64bit clean here
return pStream->total_in + nTotalIn64;
}
sal_Int64 Deflater::getTotalOut() const
{
return pStream->total_out; // FIXME64: zlib doesn't look 64bit clean here
return pStream->total_out + nTotalOut64;
}
void Deflater::reset( )
{
diff --git a/package/source/zipapi/MemoryByteGrabber.hxx b/package/source/zipapi/MemoryByteGrabber.hxx
index 8dcf7f0..a4d9f0b 100644
--- a/package/source/zipapi/MemoryByteGrabber.hxx
+++ b/package/source/zipapi/MemoryByteGrabber.hxx
@@ -81,6 +81,38 @@ public:
nInt32 |= ( mpBuffer [mnCurrent++] & 0xFF ) << 24;
return nInt32;
}
sal_Int64 ReadInt64()
{
if (mnCurrent + 8 > mnEnd)
return 0;
sal_Int64 nInt64 = mpBuffer[mnCurrent++] & 0xFF;
nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 8;
nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 16;
nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 24;
nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 32;
nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 40;
nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 48;
nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 56;
return nInt64;
}
sal_uInt64 ReadUInt64()
{
if (mnCurrent + 8 > mnEnd)
return 0;
sal_uInt64 nInt64 = mpBuffer[mnCurrent++] & 0xFF;
nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 8;
nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 16;
nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 24;
nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 32;
nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 40;
nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 48;
nInt64 |= static_cast<sal_Int64>(mpBuffer[mnCurrent++] & 0xFF) << 56;
return nInt64;
}
};
#endif
diff --git a/package/source/zipapi/ZipFile.cxx b/package/source/zipapi/ZipFile.cxx
index f7cb9df..41325f4 100644
--- a/package/source/zipapi/ZipFile.cxx
+++ b/package/source/zipapi/ZipFile.cxx
@@ -799,8 +799,6 @@ void ZipFile::readLOC( ZipEntry &rEntry )
rEntry.nOffset = aGrabber.getPosition() + nPathLen + nExtraLen;
// FIXME64: need to read 64bit LOC
bool bBroken = false;
try
@@ -942,26 +940,13 @@ sal_Int32 ZipFile::readCEN()
aEntry.nTime = aMemGrabber.ReadInt32();
aEntry.nCrc = aMemGrabber.ReadInt32();
sal_uInt32 nCompressedSize = aMemGrabber.ReadUInt32();
sal_uInt32 nSize = aMemGrabber.ReadUInt32();
sal_uInt64 nCompressedSize = aMemGrabber.ReadUInt32();
sal_uInt64 nSize = aMemGrabber.ReadUInt32();
aEntry.nPathLen = aMemGrabber.ReadInt16();
aEntry.nExtraLen = aMemGrabber.ReadInt16();
nCommentLen = aMemGrabber.ReadInt16();
aMemGrabber.skipBytes ( 8 );
sal_uInt32 nOffset = aMemGrabber.ReadUInt32();
// FIXME64: need to read the 64bit header instead
if ( nSize == 0xffffffff ||
nOffset == 0xffffffff ||
nCompressedSize == 0xffffffff ) {
throw ZipException("PK64 zip file entry" );
}
aEntry.nCompressedSize = nCompressedSize;
aEntry.nSize = nSize;
aEntry.nOffset = nOffset;
aEntry.nOffset += nLocPos;
aEntry.nOffset *= -1;
sal_uInt64 nOffset = aMemGrabber.ReadUInt32();
if ( aEntry.nPathLen < 0 )
throw ZipException("unexpected name length" );
@@ -983,7 +968,20 @@ sal_Int32 ZipFile::readCEN()
if ( !::comphelper::OStorageHelper::IsValidZipEntryFileName( aEntry.sPath, true ) )
throw ZipException("Zip entry has an invalid name." );
aMemGrabber.skipBytes( aEntry.nPathLen + aEntry.nExtraLen + nCommentLen );
aMemGrabber.skipBytes(aEntry.nPathLen);
if (aEntry.nExtraLen>0)
{
readExtraFields(aMemGrabber, aEntry.nExtraLen, nSize, nCompressedSize, &nOffset);
}
aEntry.nCompressedSize = nCompressedSize;
aEntry.nSize = nSize;
aEntry.nOffset = nOffset;
aEntry.nOffset += nLocPos;
aEntry.nOffset *= -1;
aMemGrabber.skipBytes(nCommentLen);
aEntries[aEntry.sPath] = aEntry;
}
@@ -998,6 +996,40 @@ sal_Int32 ZipFile::readCEN()
return nCenPos;
}
void ZipFile::readExtraFields(MemoryByteGrabber& aMemGrabber, sal_Int16 nExtraLen,
sal_uInt64& nSize, sal_uInt64& nCompressedSize, sal_uInt64* nOffset)
{
while (nExtraLen > 0) // Extensible data fields
{
sal_Int16 nheaderID = aMemGrabber.ReadInt16();
sal_Int16 dataSize = aMemGrabber.ReadInt16();
if (nheaderID == 1) // Load Zip64 Extended Information Extra Field
{
// Datasize should be 28byte but some files have less (maybe non standard?)
nSize = aMemGrabber.ReadUInt64();
sal_Int16 nReadSize = 8;
if (dataSize >= 16)
{
nCompressedSize = aMemGrabber.ReadUInt64();
nReadSize = 16;
if (dataSize >= 24 && nOffset)
{
*nOffset = aMemGrabber.ReadUInt64();
nReadSize = 24;
// 4 byte should be "Disk Start Number" but we not need it
}
}
if (dataSize > nReadSize)
aMemGrabber.skipBytes(dataSize - nReadSize);
}
else
{
aMemGrabber.skipBytes(dataSize);
}
nExtraLen -= dataSize + 4;
}
}
void ZipFile::recover()
{
::osl::MutexGuard aGuard( m_aMutexHolder->GetMutex() );
@@ -1028,6 +1060,7 @@ void ZipFile::recover()
{
if ( nPos < nBufSize - 30 && pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 3 && pBuffer[nPos+3] == 4 )
{
//PK34: Local file header
ZipEntry aEntry;
Sequence<sal_Int8> aTmpBuffer(&(pBuffer[nPos+4]), 26);
MemoryByteGrabber aMemGrabber(aTmpBuffer);
@@ -1043,19 +1076,11 @@ void ZipFile::recover()
{
aEntry.nTime = aMemGrabber.ReadInt32();
aEntry.nCrc = aMemGrabber.ReadInt32();
sal_uInt32 nCompressedSize = aMemGrabber.ReadUInt32();
sal_uInt32 nSize = aMemGrabber.ReadUInt32();
sal_uInt64 nCompressedSize = aMemGrabber.ReadUInt32();
sal_uInt64 nSize = aMemGrabber.ReadUInt32();
aEntry.nPathLen = aMemGrabber.ReadInt16();
aEntry.nExtraLen = aMemGrabber.ReadInt16();
// FIXME64: need to read the 64bit header instead
if ( nSize == 0xffffffff ||
nCompressedSize == 0xffffffff ) {
throw ZipException("PK64 zip file entry" );
}
aEntry.nCompressedSize = nCompressedSize;
aEntry.nSize = nSize;
sal_Int32 nDescrLength =
( aEntry.nMethod == DEFLATED && ( aEntry.nFlag & 8 ) ) ? 16 : 0;
@@ -1080,6 +1105,35 @@ void ZipFile::recover()
aEntry.nPathLen = static_cast< sal_Int16 >(aFileName.getLength());
}
// read 64bit header
if (aEntry.nExtraLen > 0)
{
Sequence<sal_Int8>* aExtraBuffer;
if (nPos + 30 + aEntry.nPathLen + aEntry.nExtraLen <= nBufSize)
{
Sequence<sal_Int8> aTmpBuffer2(
&(pBuffer[nPos + 30 + aEntry.nPathLen]),
aEntry.nExtraLen);
aExtraBuffer = &aTmpBuffer2;
}
else
{
Sequence<sal_Int8> aExtraFields;
aGrabber.seek(nGenPos + nPos + 30 + aEntry.nExtraLen);
aGrabber.readBytes(aExtraFields, aEntry.nExtraLen);
aExtraBuffer = &aExtraFields;
}
MemoryByteGrabber aMemGrabberExtra(*aExtraBuffer);
if (aEntry.nExtraLen > 0)
{
readExtraFields(aMemGrabberExtra, aEntry.nExtraLen, nSize,
nCompressedSize, nullptr);
}
}
aEntry.nCompressedSize = nCompressedSize;
aEntry.nSize = nSize;
aEntry.nOffset = nGenPos + nPos + 30 + aEntry.nPathLen + aEntry.nExtraLen;
if ( ( aEntry.nSize || aEntry.nCompressedSize ) && !checkSizeAndCRC( aEntry ) )
@@ -1098,16 +1152,35 @@ void ZipFile::recover()
}
else if (pBuffer[nPos] == 'P' && pBuffer[nPos+1] == 'K' && pBuffer[nPos+2] == 7 && pBuffer[nPos+3] == 8 )
{
//PK78: Data descriptor
sal_Int64 nCompressedSize, nSize;
Sequence<sal_Int8> aTmpBuffer(&(pBuffer[nPos+4]), 12);
Sequence<sal_Int8> aTmpBuffer(&(pBuffer[nPos + 4]), 12 + 8 + 4);
MemoryByteGrabber aMemGrabber(aTmpBuffer);
sal_Int32 nCRC32 = aMemGrabber.ReadInt32();
sal_uInt32 nCompressedSize32 = aMemGrabber.ReadUInt32();
sal_uInt32 nSize32 = aMemGrabber.ReadUInt32();
// FIXME64: work to be done here ...
nCompressedSize = nCompressedSize32;
nSize = nSize32;
// FIXME64: find a better way to recognize if Zip64 mode is used
// Now we check if the memory at +16 byte seems to be a signature
// if not, then probably Zip64 mode is used here, except
// if memory at +24 byte seems not to be a signature.
// Normally Data Descriptor should followed by the next Local File header
// that should start with PK34, except for the last file, then it may
// followed by Central directory that start with PK12, or
// followed by "archive decryption header" that don't have a signature.
if ((pBuffer[nPos + 16] == 'P' && pBuffer[nPos + 17] == 'K'
&& pBuffer[nPos + 19] == pBuffer[nPos + 18] + 1
&& (pBuffer[nPos + 18] == 3 || pBuffer[nPos + 18] == 1))
|| !(pBuffer[nPos + 24] == 'P' && pBuffer[nPos + 25] == 'K'
&& pBuffer[nPos + 27] == pBuffer[nPos + 26] + 1
&& (pBuffer[nPos + 26] == 3 || pBuffer[nPos + 26] == 1)))
{
nCompressedSize = aMemGrabber.ReadUInt32();
nSize = aMemGrabber.ReadUInt32();
}
else
{
nCompressedSize = aMemGrabber.ReadUInt64();
nSize = aMemGrabber.ReadUInt64();
}
for( auto& rEntry : aEntries )
{
diff --git a/package/source/zipapi/ZipOutputStream.cxx b/package/source/zipapi/ZipOutputStream.cxx
index df21f1f..402a293 100644
--- a/package/source/zipapi/ZipOutputStream.cxx
+++ b/package/source/zipapi/ZipOutputStream.cxx
@@ -84,7 +84,7 @@ void ZipOutputStream::rawCloseEntry( bool bEncrypt )
{
assert(m_pCurrentEntry && "Forgot to call writeLOC()?");
if ( m_pCurrentEntry->nMethod == DEFLATED && ( m_pCurrentEntry->nFlag & 8 ) )
writeEXT(*m_pCurrentEntry);
writeDataDescriptor(*m_pCurrentEntry);
if (bEncrypt)
m_pCurrentEntry->nMethod = STORED;
@@ -235,42 +235,54 @@ void ZipOutputStream::writeCEN( const ZipEntry &rEntry )
m_aChucker.WriteUInt32( rEntry.nCrc );
m_aChucker.WriteUInt32( getTruncated( rEntry.nCompressedSize, &bWrite64Header ) );
m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) );
m_aChucker.WriteInt16( nNameLength );
m_aChucker.WriteInt16( 0 );
sal_uInt32 nOffset32bit = getTruncated( rEntry.nOffset, &bWrite64Header );
m_aChucker.WriteInt16(nNameLength);
m_aChucker.WriteInt16( bWrite64Header? 32 : 0 ); //in ZIP64 case extra field is 32byte
m_aChucker.WriteInt16( 0 );
m_aChucker.WriteInt16( 0 );
m_aChucker.WriteInt16( 0 );
m_aChucker.WriteInt32( 0 );
m_aChucker.WriteUInt32( getTruncated( rEntry.nOffset, &bWrite64Header ) );
if( bWrite64Header )
{
// FIXME64: need to append a ZIP64 header instead of throwing
// We're about to silently lose people's data - which they are
// unlikely to appreciate so fail instead:
throw IOException( "File contains streams that are too large." );
}
m_aChucker.WriteUInt32( nOffset32bit );
Sequence < sal_Int8 > aSequence( reinterpret_cast<sal_Int8 const *>(sUTF8Name.getStr()), sUTF8Name.getLength() );
m_aChucker.WriteBytes( aSequence );
if (bWrite64Header)
{
writeExtraFields( rEntry );
}
}
void ZipOutputStream::writeEXT( const ZipEntry &rEntry )
void ZipOutputStream::writeDataDescriptor(const ZipEntry& rEntry)
{
bool bWrite64Header = false;
m_aChucker.WriteInt32( EXTSIG );
m_aChucker.WriteUInt32( rEntry.nCrc );
m_aChucker.WriteUInt32( getTruncated( rEntry.nCompressedSize, &bWrite64Header ) );
m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) );
if( bWrite64Header )
// For ZIP64(tm) format archives, the compressed and uncompressed sizes are 8 bytes each.
// TODO: Not sure if this is the "when ZIP64(tm) format is used"
bWrite64Header = rEntry.nCompressedSize >= 0x100000000 || rEntry.nSize >= 0x100000000;
if (!bWrite64Header)
{
// FIXME64: need to append a ZIP64 header instead of throwing
// We're about to silently lose people's data - which they are
// unlikely to appreciate so fail instead:
throw IOException( "File contains streams that are too large." );
m_aChucker.WriteUInt32( static_cast<sal_uInt32>(rEntry.nCompressedSize) );
m_aChucker.WriteUInt32( static_cast<sal_uInt32>(rEntry.nSize) );
}
else
{
m_aChucker.WriteUInt64( rEntry.nCompressedSize );
m_aChucker.WriteUInt64( rEntry.nSize );
}
}
void ZipOutputStream::writeExtraFields(const ZipEntry& rEntry)
{
//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( rEntry.nOffset );
m_aChucker.WriteInt32( 0 ); //Number of the disk on which this file starts
}
void ZipOutputStream::writeLOC( ZipEntry *pEntry, bool bEncrypt )
@@ -312,20 +324,17 @@ void ZipOutputStream::writeLOC( ZipEntry *pEntry, bool bEncrypt )
m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) );
}
m_aChucker.WriteInt16( nNameLength );
m_aChucker.WriteInt16( 0 );
if( bWrite64Header )
{
// FIXME64: need to append a ZIP64 header instead of throwing
// We're about to silently lose people's data - which they are
// unlikely to appreciate so fail instead:
throw IOException( "File contains streams that are too large." );
}
m_aChucker.WriteInt16( bWrite64Header ? 32 : 0 );
Sequence < sal_Int8 > aSequence( reinterpret_cast<sal_Int8 const *>(sUTF8Name.getStr()), sUTF8Name.getLength() );
m_aChucker.WriteBytes( aSequence );
m_pCurrentEntry->nOffset = m_aChucker.GetPosition() - (LOCHDR + nNameLength);
if (bWrite64Header)
{
writeExtraFields(rEntry);
}
}
sal_uInt32 ZipOutputStream::getCurrentDosTime()
diff --git a/sc/qa/unit/data/xlsx/tdf82984_zip64XLSXImport.xlsx b/sc/qa/unit/data/xlsx/tdf82984_zip64XLSXImport.xlsx
new file mode 100644
index 0000000..1e03f68
--- /dev/null
+++ b/sc/qa/unit/data/xlsx/tdf82984_zip64XLSXImport.xlsx
Binary files differ
diff --git a/sc/qa/unit/subsequent_filters_test3.cxx b/sc/qa/unit/subsequent_filters_test3.cxx
index dd9f92e..c2e76d2 100644
--- a/sc/qa/unit/subsequent_filters_test3.cxx
+++ b/sc/qa/unit/subsequent_filters_test3.cxx
@@ -1706,6 +1706,12 @@ CPPUNIT_TEST_FIXTURE(ScFiltersTest3, testTdf104502_hiddenColsCountedInPageCount)
CPPUNIT_ASSERT_EQUAL(SCROW(55), nEndRow);
}
CPPUNIT_TEST_FIXTURE(ScFiltersTest3, testTdf82984_zip64XLSXImport)
{
// Without the fix in place, it would have crashed at import time
createScDoc("xlsx/tdf82984_zip64XLSXImport.xlsx");
}
CPPUNIT_TEST_FIXTURE(ScFiltersTest3, testTdf108188_pagestyle)
{
createScDoc("ods/tdf108188_pagestyle.ods");
diff --git a/tools/source/stream/strmwnt.cxx b/tools/source/stream/strmwnt.cxx
index 57f7c8b..d7d3a73 100644
--- a/tools/source/stream/strmwnt.cxx
+++ b/tools/source/stream/strmwnt.cxx
@@ -152,24 +152,29 @@ sal_uInt64 SvFileStream::SeekPos(sal_uInt64 const nPos)
{
// check if a truncated STREAM_SEEK_TO_END was passed
assert(nPos != SAL_MAX_UINT32);
DWORD nNewPos = 0;
LARGE_INTEGER nNewPos, nActPos;
nNewPos.QuadPart = 0;
nActPos.QuadPart = nPos;
bool result = false;
if( IsOpen() )
{
if( nPos != STREAM_SEEK_TO_END )
// 64-Bit files are not supported
nNewPos=SetFilePointer(mxFileHandle,nPos,nullptr,FILE_BEGIN);
else
nNewPos=SetFilePointer(mxFileHandle,0L,nullptr,FILE_END);
if( nNewPos == 0xFFFFFFFF )
{
SetError(::GetSvError( GetLastError() ) );
nNewPos = 0;
result = SetFilePointerEx(mxFileHandle, nActPos, &nNewPos, FILE_BEGIN);
}
else
{
result = SetFilePointerEx(mxFileHandle, nNewPos, &nNewPos, FILE_END);
}
if (!result)
{
SetError(::GetSvError(GetLastError()));
return 0;
}
}
else
SetError( SVSTREAM_GENERALERROR );
return static_cast<sal_uInt64>(nNewPos);
return static_cast<sal_uInt64>(nNewPos.QuadPart);
}
void SvFileStream::FlushData()
diff --git a/unotools/source/streaming/streamwrap.cxx b/unotools/source/streaming/streamwrap.cxx
index 7cb90aa..593f7a1 100644
--- a/unotools/source/streaming/streamwrap.cxx
+++ b/unotools/source/streaming/streamwrap.cxx
@@ -176,7 +176,7 @@ void SAL_CALL OSeekableInputStreamWrapper::seek( sal_Int64 _nLocation )
std::scoped_lock aGuard( m_aMutex );
checkConnected();
m_pSvStream->Seek(static_cast<sal_uInt32>(_nLocation));
m_pSvStream->Seek(static_cast<sal_uInt64>(_nLocation));
checkError();
}