diff options
-rw-r--r-- | include/package/Deflater.hxx | 2 | ||||
-rw-r--r-- | package/inc/ByteChucker.hxx | 30 | ||||
-rw-r--r-- | package/inc/ZipFile.hxx | 4 | ||||
-rw-r--r-- | package/inc/ZipOutputStream.hxx | 3 | ||||
-rw-r--r-- | package/qa/cppunit/data/export64.zip | bin | 0 -> 3556 bytes | |||
-rw-r--r-- | package/qa/cppunit/test_package.cxx | 49 | ||||
-rw-r--r-- | package/source/zipapi/ByteChucker.cxx | 2 | ||||
-rw-r--r-- | package/source/zipapi/Deflater.cxx | 18 | ||||
-rw-r--r-- | package/source/zipapi/MemoryByteGrabber.hxx | 32 | ||||
-rw-r--r-- | package/source/zipapi/ZipFile.cxx | 143 | ||||
-rw-r--r-- | package/source/zipapi/ZipOutputStream.cxx | 69 | ||||
-rw-r--r-- | sc/qa/unit/data/xlsx/tdf82984_zip64XLSXImport.xlsx | bin | 0 -> 4708 bytes | |||
-rw-r--r-- | sc/qa/unit/subsequent_filters_test3.cxx | 6 | ||||
-rw-r--r-- | tools/source/stream/strmwnt.cxx | 23 | ||||
-rw-r--r-- | unotools/source/streaming/streamwrap.cxx | 2 |
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 Binary files differnew file mode 100644 index 000000000000..b30326696794 --- /dev/null +++ b/package/qa/cppunit/data/export64.zip 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 Binary files differnew file mode 100644 index 000000000000..1e03f68b7f57 --- /dev/null +++ b/sc/qa/unit/data/xlsx/tdf82984_zip64XLSXImport.xlsx 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(); } |