package/inc/ZipOutputStream.hxx                |    2 +-
 package/source/zipapi/ZipOutputStream.cxx      |   24 ++++++++++++++++--------
 package/source/zippackage/ZipPackage.cxx       |    6 ++++--
 package/source/zippackage/ZipPackageStream.cxx |   17 +++++++++++++++--
 4 files changed, 36 insertions(+), 13 deletions(-)

New commits:
commit 4c9340bdaf853c111ca1d5aaeeea5d861d21d4bf
Author:     Michael Stahl <[email protected]>
AuthorDate: Fri Sep 19 20:06:14 2025 +0200
Commit:     Noel Grandin <[email protected]>
CommitDate: Wed Oct 1 20:02:15 2025 +0200

    tdf#167205 package: fix writing Zip64 local headers
    
    This would be reported on reading as an 8-byte gap at the position where
    a 32-bit DD for the entry would end.
    
    The problem is that if flag bit 3 is set, and the uncompressed size
    exceeds 2^32, writeDataDescriptor() will write 8-byte sizes, but the
    Zip64 extension header is missing - the latter should also write 0 even
    though the APPNOTE doens't appear to require it.
    
          4.3.9.2 [...] When extracting, if
          the zip64 extended information extra field is present for
          the file the compressed and uncompressed sizes will be 8
          byte values.
    
    Also make sure that the ZipEntry nSize member actually contains the
    uncompressed size, which is somewhat tricky, but is required in
    writeLOC() to check if Zip64 is needed.  (Assume that the DEFLATEd size
    is never larger than uncompressed, which is true for every XML file).
    
    Change-Id: I611e6ffda12d8e6953e9306dc4a4407be8e725a0
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191185
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <[email protected]>
    Reviewed-by: Michael Stahl <[email protected]>
    (cherry picked from commit abfdc11fa45610f38d00999f66416c66a868426a)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/191736
    Tested-by: Jenkins CollaboraOffice <[email protected]>

diff --git a/package/inc/ZipOutputStream.hxx b/package/inc/ZipOutputStream.hxx
index 5b04a0e41dc8..8ffc623e564b 100644
--- a/package/inc/ZipOutputStream.hxx
+++ b/package/inc/ZipOutputStream.hxx
@@ -80,7 +80,7 @@ private:
     /// @throws css::io::IOException
     /// @throws css::uno::RuntimeException
     void writeDataDescriptor( const ZipEntry &rEntry );
-    void writeExtraFields( const ZipEntry& rEntry );
+    void writeExtraFields(const ZipEntry& rEntry, bool isLOCWithDD);
 
     // ScheduledThread handling helpers
     void 
consumeScheduledThreadTaskEntry(std::unique_ptr<ZipOutputEntryInThread> 
pCandidate);
diff --git a/package/source/zipapi/ZipOutputStream.cxx 
b/package/source/zipapi/ZipOutputStream.cxx
index da265c303b13..2217ce518aa8 100644
--- a/package/source/zipapi/ZipOutputStream.cxx
+++ b/package/source/zipapi/ZipOutputStream.cxx
@@ -64,7 +64,12 @@ void ZipOutputStream::setEntry(ZipEntry& rEntry)
     if (rEntry.nSize == -1 || rEntry.nCompressedSize == -1 ||
         rEntry.nCrc == -1)
     {
-        rEntry.nSize = rEntry.nCompressedSize = 0;
+        if (rEntry.nSize == -1)
+        {
+            assert(false); // how to get here
+            rEntry.nSize = 0;
+        }
+        rEntry.nCompressedSize = 0;
         rEntry.nFlag |= 8;
     }
 }
@@ -251,7 +256,7 @@ void ZipOutputStream::writeCEN( const ZipEntry &rEntry )
 
     if (bWrite64Header)
     {
-        writeExtraFields( rEntry );
+        writeExtraFields(rEntry, false);
     }
 }
 
@@ -276,13 +281,13 @@ void ZipOutputStream::writeDataDescriptor(const ZipEntry& 
rEntry)
     }
 }
 
-void ZipOutputStream::writeExtraFields(const ZipEntry& rEntry)
+void ZipOutputStream::writeExtraFields(const ZipEntry& rEntry, bool const 
isLOCWithDD)
 {
     //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(isLOCWithDD ? 0 : rEntry.nSize);
+    m_aChucker.WriteUInt64(isLOCWithDD ? 0 : rEntry.nCompressedSize);
     m_aChucker.WriteUInt64( rEntry.nOffset );
     m_aChucker.WriteInt32( 0 );  //Number of the disk on which this file starts
 }
@@ -311,6 +316,9 @@ void ZipOutputStream::writeLOC(std::unique_ptr<ZipEntry>&& 
pEntry, bool bEncrypt
         m_aChucker.WriteInt16( rEntry.nMethod );
 
     bool bWrite64Header = false;
+    // getTruncated must always be called to init bWrite64Header!
+    auto const nTruncCompressedSize{getTruncated(rEntry.nCompressedSize, 
&bWrite64Header)};
+    auto const nTruncSize{getTruncated(rEntry.nSize, &bWrite64Header)};
 
     m_aChucker.WriteUInt32( rEntry.nTime );
     if ((rEntry.nFlag & 8) == 8 )
@@ -322,8 +330,8 @@ void ZipOutputStream::writeLOC(std::unique_ptr<ZipEntry>&& 
pEntry, bool bEncrypt
     else
     {
         m_aChucker.WriteUInt32( rEntry.nCrc );
-        m_aChucker.WriteUInt32( getTruncated( rEntry.nCompressedSize, 
&bWrite64Header ) );
-        m_aChucker.WriteUInt32( getTruncated( rEntry.nSize, &bWrite64Header ) 
);
+        m_aChucker.WriteUInt32(nTruncCompressedSize);
+        m_aChucker.WriteUInt32(nTruncSize);
     }
     m_aChucker.WriteInt16( nNameLength );
     m_aChucker.WriteInt16( bWrite64Header ? 32 : 0 );
@@ -335,7 +343,7 @@ void ZipOutputStream::writeLOC(std::unique_ptr<ZipEntry>&& 
pEntry, bool bEncrypt
 
     if (bWrite64Header)
     {
-        writeExtraFields(rEntry);
+        writeExtraFields(rEntry, (rEntry.nFlag & 8));
     }
 }
 
diff --git a/package/source/zippackage/ZipPackage.cxx 
b/package/source/zippackage/ZipPackage.cxx
index 18fd18a2c6a5..5fd8cc3770c0 100644
--- a/package/source/zippackage/ZipPackage.cxx
+++ b/package/source/zippackage/ZipPackage.cxx
@@ -1178,13 +1178,14 @@ void ZipPackage::WriteManifest( ZipOutputStream& 
aZipOut, const std::vector< uno
     pEntry->sPath = "META-INF/manifest.xml";
     pEntry->nMethod = DEFLATED;
     pEntry->nCrc = -1;
-    pEntry->nSize = pEntry->nCompressedSize = -1;
+    pEntry->nCompressedSize = -1;
     pEntry->nTime = ZipOutputStream::getCurrentDosTime();
 
     xWriter->writeManifestSequence ( pBuffer,  
comphelper::containerToSequence(aManList) );
 
     sal_Int32 nBufferLength = static_cast < sal_Int32 > ( 
pBuffer->getPosition() );
     pBuffer->realloc( nBufferLength );
+    pEntry->nSize = nBufferLength;
 
     // the manifest.xml is never encrypted - so pass an empty reference
     ZipOutputStream::setEntry(*pEntry);
@@ -1204,7 +1205,7 @@ void ZipPackage::WriteContentTypes( ZipOutputStream& 
aZipOut, const std::vector<
     pEntry->sPath = "[Content_Types].xml";
     pEntry->nMethod = DEFLATED;
     pEntry->nCrc = -1;
-    pEntry->nSize = pEntry->nCompressedSize = -1;
+    pEntry->nCompressedSize = -1;
     pEntry->nTime = ZipOutputStream::getCurrentDosTime();
 
     // Add default entries, the count must be updated manually when appending.
@@ -1245,6 +1246,7 @@ void ZipPackage::WriteContentTypes( ZipOutputStream& 
aZipOut, const std::vector<
 
     sal_Int32 nBufferLength = static_cast < sal_Int32 > ( 
pBuffer->getPosition() );
     pBuffer->realloc( nBufferLength );
+    pEntry->nSize = nBufferLength;
 
     // there is no encryption in this format currently
     ZipOutputStream::setEntry(*pEntry);
diff --git a/package/source/zippackage/ZipPackageStream.cxx 
b/package/source/zippackage/ZipPackageStream.cxx
index 8280a048c7c5..85f18f47709a 100644
--- a/package/source/zippackage/ZipPackageStream.cxx
+++ b/package/source/zippackage/ZipPackageStream.cxx
@@ -511,7 +511,15 @@ bool ZipPackageStream::saveChild(
         bUseNonSeekableAccess = ( xStream.is() && !xSeek.is() );
     }
 
-    if ( !bUseNonSeekableAccess )
+    if (bUseNonSeekableAccess)
+    {
+        // this should work for XUnbufferedStream/OInputCompStream at least
+        if (pTempEntry->nSize == -1)
+        {   // this is needed in writeLOC to detect Zip64
+            pTempEntry->nSize = xStream->available();
+        }
+    }
+    else
     {
         xStream = getRawData();
 
@@ -541,6 +549,11 @@ bool ZipPackageStream::saveChild(
                     m_nOwnStreamOrigSize = xSeek->getLength();
                 }
 
+                if (pTempEntry->nSize == -1)
+                {   // this is needed in writeLOC to detect Zip64
+                    pTempEntry->nSize = xSeek->getLength();
+                }
+
                 xSeek->seek ( 0 );
             }
             else
@@ -742,7 +755,7 @@ bool ZipPackageStream::saveChild(
         {
             pTempEntry->nMethod = DEFLATED;
             pTempEntry->nCrc = -1;
-            pTempEntry->nCompressedSize = pTempEntry->nSize = -1;
+            pTempEntry->nCompressedSize = -1;
         }
 
         uno::Reference< io::XSeekable > xSeek(xStream, uno::UNO_QUERY);

Reply via email to