summaryrefslogtreecommitdiff
path: root/package
diff options
context:
space:
mode:
authorAttila Szűcs <attila.szucs@collabora.com>2023-02-20 00:32:22 +0100
committerMichael Meeks <michael.meeks@collabora.com>2023-03-08 14:53:57 +0000
commitabda72eeac19b18c22f57d5443c3955a463605d7 (patch)
treea6ac56d800179a06330b834e29c31894716f929f /package
parentf1306e1d0f52716cf44a9052654af4133fa2c6eb (diff)
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>
Diffstat (limited to 'package')
-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
10 files changed, 280 insertions, 70 deletions
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()