cid#1606940 Check of thread-shared field evades lock acquisition
[LibreOffice.git] / package / source / zipapi / ZipOutputEntry.cxx
blobd2180ef0d4966b33666e673c1e4cef016a8afc2e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <ZipOutputEntry.hxx>
22 #include <com/sun/star/io/TempFile.hpp>
23 #include <com/sun/star/packages/zip/ZipConstants.hpp>
24 #include <com/sun/star/xml/crypto/CipherID.hpp>
26 #include <osl/diagnose.h>
28 #include <PackageConstants.hxx>
29 #include <ThreadedDeflater.hxx>
30 #include <ZipEntry.hxx>
31 #include <ZipFile.hxx>
32 #include <ZipPackageStream.hxx>
34 #include <algorithm>
35 #include <utility>
37 using namespace com::sun::star;
38 using namespace com::sun::star::io;
39 using namespace com::sun::star::uno;
40 using namespace com::sun::star::packages::zip::ZipConstants;
42 /** This class is used to deflate Zip entries
44 ZipOutputEntryBase::ZipOutputEntryBase(
45 css::uno::Reference< css::io::XOutputStream > xOutput,
46 uno::Reference< uno::XComponentContext > xContext,
47 ZipEntry* pEntry,
48 ZipPackageStream* pStream,
49 bool bEncrypt,
50 bool checkStream)
51 : m_xContext(std::move(xContext))
52 , m_xOutStream(std::move(xOutput))
53 , m_pCurrentEntry(pEntry)
54 , m_nDigested(0)
55 , m_pCurrentStream(pStream)
56 , m_bEncryptCurrentEntry(bEncrypt)
58 assert(pEntry);
59 assert(m_pCurrentEntry->nMethod == DEFLATED && "Use ZipPackageStream::rawWrite() for STORED entries");
60 (void)checkStream;
61 assert(!checkStream || m_xOutStream.is());
62 if (m_bEncryptCurrentEntry)
64 m_xCipherContext = ZipFile::StaticGetCipher( m_xContext, pStream->GetEncryptionData(), true );
65 if (pStream->GetEncryptionData()->m_oCheckAlg)
67 assert(pStream->GetEncryptionData()->m_nEncAlg != xml::crypto::CipherID::AES_GCM_W3C);
68 m_xDigestContext = ZipFile::StaticGetDigestContextForChecksum(m_xContext, pStream->GetEncryptionData());
73 void ZipOutputEntryBase::closeEntry()
75 finishDeflater();
77 if ((m_pCurrentEntry->nFlag & 8) == 0)
79 if (m_pCurrentEntry->nSize != getDeflaterTotalIn())
81 OSL_FAIL("Invalid entry size");
83 if (m_pCurrentEntry->nCompressedSize != getDeflaterTotalOut())
85 // Different compression strategies make the merit of this
86 // test somewhat dubious
87 m_pCurrentEntry->nCompressedSize = getDeflaterTotalOut();
89 if (m_pCurrentEntry->nCrc != m_aCRC.getValue())
91 OSL_FAIL("Invalid entry CRC-32");
94 else
96 if ( !m_bEncryptCurrentEntry )
98 m_pCurrentEntry->nSize = getDeflaterTotalIn();
99 m_pCurrentEntry->nCompressedSize = getDeflaterTotalOut();
101 m_pCurrentEntry->nCrc = m_aCRC.getValue();
103 deflaterReset();
104 m_aCRC.reset();
106 if (!m_bEncryptCurrentEntry)
107 return;
109 m_xCipherContext.clear();
111 uno::Sequence< sal_Int8 > aDigestSeq;
112 if ( m_xDigestContext.is() )
114 aDigestSeq = m_xDigestContext->finalizeDigestAndDispose();
115 m_xDigestContext.clear();
118 if ( m_pCurrentStream )
119 m_pCurrentStream->setDigest( aDigestSeq );
122 void ZipOutputEntryBase::processDeflated( const uno::Sequence< sal_Int8 >& deflateBuffer, sal_Int32 nLength )
124 if ( nLength > 0 )
126 uno::Sequence< sal_Int8 > aTmpBuffer( deflateBuffer.getConstArray(), nLength );
127 if (m_bEncryptCurrentEntry && m_xCipherContext.is())
129 // Need to update our digest before encryption...
130 sal_Int32 nDiff = n_ConstDigestLength - m_nDigested;
131 if (m_xDigestContext.is() && nDiff)
133 sal_Int32 nEat = ::std::min( nLength, nDiff );
134 uno::Sequence< sal_Int8 > aTmpSeq( aTmpBuffer.getConstArray(), nEat );
135 m_xDigestContext->updateDigest( aTmpSeq );
136 m_nDigested = m_nDigested + static_cast< sal_Int16 >( nEat );
139 // FIXME64: uno::Sequence not 64bit safe.
140 uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->convertWithCipherContext( aTmpBuffer );
142 m_xOutStream->writeBytes( aEncryptionBuffer );
144 // the sizes as well as checksum for encrypted streams is calculated here
145 m_pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
146 m_pCurrentEntry->nSize = m_pCurrentEntry->nCompressedSize;
147 m_aCRC.update( aEncryptionBuffer );
149 else
151 m_xOutStream->writeBytes ( aTmpBuffer );
155 if (!(isDeflaterFinished() && m_bEncryptCurrentEntry && m_xCipherContext.is()))
156 return;
158 // FIXME64: sequence not 64bit safe.
159 uno::Sequence< sal_Int8 > aEncryptionBuffer = m_xCipherContext->finalizeCipherContextAndDispose();
160 if ( aEncryptionBuffer.hasElements() )
162 m_xOutStream->writeBytes( aEncryptionBuffer );
164 // the sizes as well as checksum for encrypted streams are calculated here
165 m_pCurrentEntry->nCompressedSize += aEncryptionBuffer.getLength();
166 m_pCurrentEntry->nSize = m_pCurrentEntry->nCompressedSize;
167 m_aCRC.update( aEncryptionBuffer );
171 void ZipOutputEntryBase::processInput( const uno::Sequence< sal_Int8 >& rBuffer )
173 if (!m_bEncryptCurrentEntry)
174 m_aCRC.updateSegment(rBuffer, rBuffer.getLength());
177 ZipOutputEntry::ZipOutputEntry(
178 const css::uno::Reference< css::io::XOutputStream >& rxOutput,
179 const uno::Reference< uno::XComponentContext >& rxContext,
180 ZipEntry* pEntry,
181 ZipPackageStream* pStream,
182 bool bEncrypt,
183 bool checkStream)
184 : ZipOutputEntryBase(rxOutput, rxContext, pEntry, pStream, bEncrypt, checkStream)
185 , m_aDeflateBuffer(n_ConstBufferSize)
186 , m_aDeflater(DEFAULT_COMPRESSION, true)
190 ZipOutputEntry::ZipOutputEntry(
191 const css::uno::Reference< css::io::XOutputStream >& rxOutput,
192 const uno::Reference< uno::XComponentContext >& rxContext,
193 ZipEntry* pEntry,
194 ZipPackageStream* pStream,
195 bool bEncrypt)
196 : ZipOutputEntry( rxOutput, rxContext, pEntry, pStream, bEncrypt, true)
200 void ZipOutputEntry::write( const Sequence< sal_Int8 >& rBuffer )
202 if (!m_aDeflater.finished())
204 m_aDeflater.setInputSegment(rBuffer);
205 while (!m_aDeflater.needsInput())
206 doDeflate();
207 processInput(rBuffer);
211 void ZipOutputEntry::doDeflate()
213 sal_Int32 nLength = m_aDeflater.doDeflateSegment(m_aDeflateBuffer, m_aDeflateBuffer.getLength());
214 processDeflated( m_aDeflateBuffer, nLength );
217 void ZipOutputEntry::finishDeflater()
219 m_aDeflater.finish();
220 while (!m_aDeflater.finished())
221 doDeflate();
224 sal_Int64 ZipOutputEntry::getDeflaterTotalIn() const
226 return m_aDeflater.getTotalIn();
229 sal_Int64 ZipOutputEntry::getDeflaterTotalOut() const
231 return m_aDeflater.getTotalOut();
234 void ZipOutputEntry::deflaterReset()
236 m_aDeflater.reset();
239 bool ZipOutputEntry::isDeflaterFinished() const
241 return m_aDeflater.finished();
245 ZipOutputEntryInThread::ZipOutputEntryInThread(
246 const uno::Reference< uno::XComponentContext >& rxContext,
247 std::unique_ptr<ZipEntry>&& pEntry,
248 ZipPackageStream* pStream,
249 bool bEncrypt)
250 : ZipOutputEntry( uno::Reference< css::io::XOutputStream >(), rxContext, pEntry.get(), pStream, bEncrypt, false )
251 , m_pOwnedZipEntry(std::move(pEntry))
252 , m_bFinished(false)
256 void ZipOutputEntryInThread::createBufferFile()
258 assert(!m_xOutStream && !m_xTempFile &&
259 "should only be called in the threaded mode where there is no existing stream yet");
260 m_xTempFile = new utl::TempFileFastService;
261 m_xOutStream = m_xTempFile->getOutputStream();
264 void ZipOutputEntryInThread::closeBufferFile()
266 m_xOutStream->closeOutput();
267 m_xOutStream.clear();
270 void ZipOutputEntryInThread::deleteBufferFile()
272 assert(!m_xOutStream.is() && m_xTempFile);
273 m_xTempFile.clear();
276 uno::Reference< io::XInputStream > ZipOutputEntryInThread::getData() const
278 return m_xTempFile->getInputStream();
281 class ZipOutputEntryInThread::Task : public comphelper::ThreadTask
283 ZipOutputEntryInThread *mpEntry;
284 uno::Reference< io::XInputStream > mxInStream;
286 public:
287 Task( const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, ZipOutputEntryInThread *pEntry,
288 uno::Reference< io::XInputStream > xInStream )
289 : comphelper::ThreadTask(pTag)
290 , mpEntry(pEntry)
291 , mxInStream(std::move(xInStream))
294 private:
295 virtual void doWork() override
299 mpEntry->createBufferFile();
300 mpEntry->writeStream(mxInStream);
301 mxInStream.clear();
302 mpEntry->closeBufferFile();
303 mpEntry->setFinished();
305 catch (...)
307 mpEntry->setParallelDeflateException(std::current_exception());
310 if (mpEntry->m_xOutStream.is())
311 mpEntry->closeBufferFile();
312 if (mpEntry->m_xTempFile)
313 mpEntry->deleteBufferFile();
315 catch (uno::Exception const&)
318 mpEntry->setFinished();
323 std::unique_ptr<comphelper::ThreadTask> ZipOutputEntryInThread::createTask(
324 const std::shared_ptr<comphelper::ThreadTaskTag>& pTag,
325 const uno::Reference< io::XInputStream >& xInStream )
327 return std::make_unique<Task>(pTag, this, xInStream);
330 void ZipOutputEntry::writeStream(const uno::Reference< io::XInputStream >& xInStream)
332 sal_Int32 nLength = 0;
333 uno::Sequence< sal_Int8 > aSeq(n_ConstBufferSize);
336 nLength = xInStream->readBytes(aSeq, n_ConstBufferSize);
337 if (nLength != n_ConstBufferSize)
338 aSeq.realloc(nLength);
340 write(aSeq);
342 while (nLength == n_ConstBufferSize);
343 closeEntry();
347 ZipOutputEntryParallel::ZipOutputEntryParallel(
348 const css::uno::Reference< css::io::XOutputStream >& rxOutput,
349 const uno::Reference< uno::XComponentContext >& rxContext,
350 ZipEntry* pEntry,
351 ZipPackageStream* pStream,
352 bool bEncrypt)
353 : ZipOutputEntryBase(rxOutput, rxContext, pEntry, pStream, bEncrypt, true)
354 , totalIn(0)
355 , totalOut(0)
356 , finished(false)
360 void ZipOutputEntryParallel::writeStream(const uno::Reference< io::XInputStream >& xInStream)
362 ZipUtils::ThreadedDeflater deflater( DEFAULT_COMPRESSION );
363 deflater.deflateWrite(xInStream,
364 [this](const uno::Sequence< sal_Int8 >& rBuffer, sal_Int32 nLen) {
365 if (!m_bEncryptCurrentEntry)
366 m_aCRC.updateSegment(rBuffer, nLen);
368 [this](const uno::Sequence< sal_Int8 >& rBuffer, sal_Int32 nLen) {
369 processDeflated(rBuffer, nLen);
372 finished = true;
373 processDeflated( uno::Sequence< sal_Int8 >(), 0 ); // finish encrypting, etc.
374 totalIn = deflater.getTotalIn();
375 totalOut = deflater.getTotalOut();
376 closeEntry();
379 void ZipOutputEntryParallel::finishDeflater()
381 // ThreadedDeflater is called synchronously in one call, so nothing to do here.
384 sal_Int64 ZipOutputEntryParallel::getDeflaterTotalIn() const
386 return totalIn;
389 sal_Int64 ZipOutputEntryParallel::getDeflaterTotalOut() const
391 return totalOut;
394 void ZipOutputEntryParallel::deflaterReset()
396 totalIn = 0;
397 totalOut = 0;
398 finished = false;
401 bool ZipOutputEntryParallel::isDeflaterFinished() const
403 return finished;
406 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */