include/package/Deflater.hxx                       |    2 
 package/inc/ByteChucker.hxx                        |   30 ++++
 package/inc/ZipFile.hxx                            |    4 
 package/inc/ZipOutputStream.hxx                    |    3 
 package/qa/cppunit/data/export64.zip               |binary
 package/qa/cppunit/test_package.cxx                |   49 +++++++
 package/source/zipapi/ByteChucker.cxx              |    2 
 package/source/zipapi/Deflater.cxx                 |   18 ++
 package/source/zipapi/MemoryByteGrabber.hxx        |   32 ++++
 package/source/zipapi/ZipFile.cxx                  |  143 +++++++++++++++------
 package/source/zipapi/ZipOutputStream.cxx          |   69 +++++-----
 sc/qa/unit/data/xlsx/tdf82984_zip64XLSXImport.xlsx |binary
 sc/qa/unit/subsequent_filters_test3.cxx            |    6 
 tools/source/stream/strmwnt.cxx                    |   23 ++-
 unotools/source/streaming/streamwrap.cxx           |    2 
 15 files changed, 303 insertions(+), 80 deletions(-)

New commits:
commit abda72eeac19b18c22f57d5443c3955a463605d7
Author:     Attila Szűcs <attila.sz...@collabora.com>
AuthorDate: Mon Feb 20 00:32:22 2023 +0100
Commit:     Michael Meeks <michael.me...@collabora.com>
CommitDate: Wed Mar 8 14:53:57 2023 +0000

    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.me...@collabora.com>

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
Binary files /dev/null and b/package/qa/cppunit/data/export64.zip 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
Binary files /dev/null and b/sc/qa/unit/data/xlsx/tdf82984_zip64XLSXImport.xlsx 
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();
 }
 

Reply via email to