summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/package/Deflater.hxx2
-rw-r--r--package/inc/ByteChucker.hxx30
-rw-r--r--package/inc/ZipFile.hxx4
-rw-r--r--package/inc/ZipOutputStream.hxx3
-rw-r--r--package/qa/cppunit/data/export64.zipbin0 -> 3556 bytes
-rw-r--r--package/qa/cppunit/test_package.cxx49
-rw-r--r--package/source/zipapi/ByteChucker.cxx2
-rw-r--r--package/source/zipapi/Deflater.cxx18
-rw-r--r--package/source/zipapi/MemoryByteGrabber.hxx32
-rw-r--r--package/source/zipapi/ZipFile.cxx143
-rw-r--r--package/source/zipapi/ZipOutputStream.cxx69
-rw-r--r--sc/qa/unit/data/xlsx/tdf82984_zip64XLSXImport.xlsxbin0 -> 4708 bytes
-rw-r--r--sc/qa/unit/subsequent_filters_test3.cxx6
-rw-r--r--tools/source/stream/strmwnt.cxx23
-rw-r--r--unotools/source/streaming/streamwrap.cxx2
15 files changed, 303 insertions, 80 deletions
diff --git a/include/package/Deflater.hxx b/include/package/Deflater.hxx
index 3cd528bf5afd..2a5c9d103260 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 707b678ff2a5..c502ad6b5c42 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 7fe15f70ff99..f6b184994729 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 f55ef59a8880..b31af9d3d5c4 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 000000000000..b30326696794
--- /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 022d8a9eea53..911e0ea60319 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 6c5994aa6a2f..fe1f6a868503 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 5c0fd70121fe..2c66253fd0ed 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 8dcf7f067064..a4d9f0b1ba49 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 f7cb9df4d309..41325f47e38f 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 df21f1ffeb13..402a2930c0e2 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,44 +235,56 @@ 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)
+ {
+ m_aChucker.WriteUInt32( static_cast<sal_uInt32>(rEntry.nCompressedSize) );
+ m_aChucker.WriteUInt32( static_cast<sal_uInt32>(rEntry.nSize) );
+ }
+ else
{
- // 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.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 )
{
assert(!m_pCurrentEntry && "Forgot to close an entry with rawCloseEntry()?");
@@ -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 000000000000..1e03f68b7f57
--- /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 dd9f92eea7ba..c2e76d2e071f 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 57f7c8b50c07..d7d3a73ed2ce 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);
+ {
+ result = SetFilePointerEx(mxFileHandle, nActPos, &nNewPos, FILE_BEGIN);
+ }
else
- nNewPos=SetFilePointer(mxFileHandle,0L,nullptr,FILE_END);
-
- if( nNewPos == 0xFFFFFFFF )
{
- SetError(::GetSvError( GetLastError() ) );
- nNewPos = 0;
+ 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 7cb90aa3a63f..593f7a1f0b28 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();
}