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>
This commit is contained in:
Attila Szűcs
2023-03-08 14:53:57 +00:00
committed by Michael Meeks
parent f1306e1d0f
commit abda72eeac
15 changed files with 304 additions and 81 deletions
+2
View File
@@ -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);
+28 -2
View File
@@ -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
+4
View File
@@ -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:
+2 -1
View File
@@ -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);
Binary file not shown.
+49
View File
@@ -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);
}
+2
View File
@@ -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() )
{
}
+16 -2
View File
@@ -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( )
{
@@ -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
+108 -35
View File
@@ -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 )
{
+39 -30
View File
@@ -84,7 +84,7 @@ void ZipOutputStream::rawCloseEntry( bool bEncrypt )
{
assert(m_pCurrentEntry && "Forgot to call writeLOC()?");
if ( m_pCurrentEntry->nMethod == DEFLATED && ( m_pCurrentEntry->nFlag & 8 ) )
writeEXT(*m_pCurrentEntry);
writeDataDescriptor(*m_pCurrentEntry);
if (bEncrypt)
m_pCurrentEntry->nMethod = STORED;
@@ -235,42 +235,54 @@ void ZipOutputStream::writeCEN( const ZipEntry &rEntry )
m_aChucker.WriteUInt32( rEntry.nCrc );
m_aChucker.WriteUInt32( getTruncated( rEntry.nCompressedSize, &bWrite64Header ) );
m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) );
m_aChucker.WriteInt16( nNameLength );
m_aChucker.WriteInt16( 0 );
sal_uInt32 nOffset32bit = getTruncated( rEntry.nOffset, &bWrite64Header );
m_aChucker.WriteInt16(nNameLength);
m_aChucker.WriteInt16( bWrite64Header? 32 : 0 ); //in ZIP64 case extra field is 32byte
m_aChucker.WriteInt16( 0 );
m_aChucker.WriteInt16( 0 );
m_aChucker.WriteInt16( 0 );
m_aChucker.WriteInt32( 0 );
m_aChucker.WriteUInt32( getTruncated( rEntry.nOffset, &bWrite64Header ) );
if( bWrite64Header )
{
// FIXME64: need to append a ZIP64 header instead of throwing
// We're about to silently lose people's data - which they are
// unlikely to appreciate so fail instead:
throw IOException( "File contains streams that are too large." );
}
m_aChucker.WriteUInt32( nOffset32bit );
Sequence < sal_Int8 > aSequence( reinterpret_cast<sal_Int8 const *>(sUTF8Name.getStr()), sUTF8Name.getLength() );
m_aChucker.WriteBytes( aSequence );
if (bWrite64Header)
{
writeExtraFields( rEntry );
}
}
void ZipOutputStream::writeEXT( const ZipEntry &rEntry )
void ZipOutputStream::writeDataDescriptor(const ZipEntry& rEntry)
{
bool bWrite64Header = false;
m_aChucker.WriteInt32( EXTSIG );
m_aChucker.WriteUInt32( rEntry.nCrc );
m_aChucker.WriteUInt32( getTruncated( rEntry.nCompressedSize, &bWrite64Header ) );
m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) );
if( bWrite64Header )
// For ZIP64(tm) format archives, the compressed and uncompressed sizes are 8 bytes each.
// TODO: Not sure if this is the "when ZIP64(tm) format is used"
bWrite64Header = rEntry.nCompressedSize >= 0x100000000 || rEntry.nSize >= 0x100000000;
if (!bWrite64Header)
{
// FIXME64: need to append a ZIP64 header instead of throwing
// We're about to silently lose people's data - which they are
// unlikely to appreciate so fail instead:
throw IOException( "File contains streams that are too large." );
m_aChucker.WriteUInt32( static_cast<sal_uInt32>(rEntry.nCompressedSize) );
m_aChucker.WriteUInt32( static_cast<sal_uInt32>(rEntry.nSize) );
}
else
{
m_aChucker.WriteUInt64( rEntry.nCompressedSize );
m_aChucker.WriteUInt64( rEntry.nSize );
}
}
void ZipOutputStream::writeExtraFields(const ZipEntry& rEntry)
{
//Could contain more fields, now we only save Zip64 extended information
m_aChucker.WriteInt16( 1 ); //id of Zip64 extended information extra field
m_aChucker.WriteInt16( 28 ); //data size of this field = 3*8+4 byte
m_aChucker.WriteUInt64( rEntry.nSize );
m_aChucker.WriteUInt64( rEntry.nCompressedSize );
m_aChucker.WriteUInt64( rEntry.nOffset );
m_aChucker.WriteInt32( 0 ); //Number of the disk on which this file starts
}
void ZipOutputStream::writeLOC( ZipEntry *pEntry, bool bEncrypt )
@@ -312,20 +324,17 @@ void ZipOutputStream::writeLOC( ZipEntry *pEntry, bool bEncrypt )
m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) );
}
m_aChucker.WriteInt16( nNameLength );
m_aChucker.WriteInt16( 0 );
if( bWrite64Header )
{
// FIXME64: need to append a ZIP64 header instead of throwing
// We're about to silently lose people's data - which they are
// unlikely to appreciate so fail instead:
throw IOException( "File contains streams that are too large." );
}
m_aChucker.WriteInt16( bWrite64Header ? 32 : 0 );
Sequence < sal_Int8 > aSequence( reinterpret_cast<sal_Int8 const *>(sUTF8Name.getStr()), sUTF8Name.getLength() );
m_aChucker.WriteBytes( aSequence );
m_pCurrentEntry->nOffset = m_aChucker.GetPosition() - (LOCHDR + nNameLength);
if (bWrite64Header)
{
writeExtraFields(rEntry);
}
}
sal_uInt32 ZipOutputStream::getCurrentDosTime()
Binary file not shown.
+6
View File
@@ -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");
+15 -10
View File
@@ -152,24 +152,29 @@ sal_uInt64 SvFileStream::SeekPos(sal_uInt64 const nPos)
{
// check if a truncated STREAM_SEEK_TO_END was passed
assert(nPos != SAL_MAX_UINT32);
DWORD nNewPos = 0;
LARGE_INTEGER nNewPos, nActPos;
nNewPos.QuadPart = 0;
nActPos.QuadPart = nPos;
bool result = false;
if( IsOpen() )
{
if( nPos != STREAM_SEEK_TO_END )
// 64-Bit files are not supported
nNewPos=SetFilePointer(mxFileHandle,nPos,nullptr,FILE_BEGIN);
else
nNewPos=SetFilePointer(mxFileHandle,0L,nullptr,FILE_END);
if( nNewPos == 0xFFFFFFFF )
{
SetError(::GetSvError( GetLastError() ) );
nNewPos = 0;
result = SetFilePointerEx(mxFileHandle, nActPos, &nNewPos, FILE_BEGIN);
}
else
{
result = SetFilePointerEx(mxFileHandle, nNewPos, &nNewPos, FILE_END);
}
if (!result)
{
SetError(::GetSvError(GetLastError()));
return 0;
}
}
else
SetError( SVSTREAM_GENERALERROR );
return static_cast<sal_uInt64>(nNewPos);
return static_cast<sal_uInt64>(nNewPos.QuadPart);
}
void SvFileStream::FlushData()
+1 -1
View File
@@ -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();
}